diff --git a/Plugins/LfpDisplayNode/CMakeLists.txt b/Plugins/LfpDisplayNode/CMakeLists.txt
index 4b6ab99ef5..7e0e53a261 100644
--- a/Plugins/LfpDisplayNode/CMakeLists.txt
+++ b/Plugins/LfpDisplayNode/CMakeLists.txt
@@ -10,9 +10,39 @@ add_sources(${PLUGIN_NAME}
LfpDisplayNode.h
LfpDisplayEditor.cpp
LfpDisplayEditor.h
+ EventDisplayInterface.cpp
+ EventDisplayInterface.h
+ LfpBitmapPlotter.h
+ LfpBitmapPlotterInfo.h
+ LfpChannelColourScheme.cpp
+ LfpChannelColourScheme.h
+ LfpChannelDisplay.cpp
+ LfpChannelDisplay.h
+ LfpChannelDisplayInfo.cpp
+ LfpChannelDisplayInfo.h
+ LfpDefaultColourScheme.cpp
+ LfpDefaultColourScheme.h
+ LfpDisplay.cpp
+ LfpDisplay.h
LfpDisplayCanvas.cpp
LfpDisplayCanvas.h
+ LfpDisplayOptions.cpp
+ LfpDisplayOptions.h
+ LfpGradientColourScheme.cpp
+ LfpGradientColourScheme.h
+ LfpMonochromaticColourScheme.cpp
+ LfpMonochromaticColourScheme.h
+ LfpTimescale.cpp
+ LfpTimescale.h
+ LfpViewport.cpp
+ LfpViewport.h
+ PerPixelBitmapPlotter.cpp
+ PerPixelBitmapPlotter.h
+ ShowHideOptionsButton.cpp
+ ShowHideOptionsButton.h
+ SupersampledBitmapPlotter.cpp
+ SupersampledBitmapPlotter.h
)
#optional: create IDE groups
-#plugin_create_filters()
\ No newline at end of file
+#plugin_create_filters()
diff --git a/Plugins/LfpDisplayNode/EventDisplayInterface.cpp b/Plugins/LfpDisplayNode/EventDisplayInterface.cpp
new file mode 100644
index 0000000000..4e68d8b245
--- /dev/null
+++ b/Plugins/LfpDisplayNode/EventDisplayInterface.cpp
@@ -0,0 +1,108 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "EventDisplayInterface.h"
+#include "LfpDisplayNode.h"
+#include "LfpDisplayCanvas.h"
+#include "ShowHideOptionsButton.h"
+#include "LfpDisplayOptions.h"
+#include "LfpTimescale.h"
+#include "LfpDisplay.h"
+#include "LfpChannelDisplay.h"
+#include "LfpChannelDisplayInfo.h"
+#include "LfpViewport.h"
+#include "LfpBitmapPlotterInfo.h"
+#include "LfpBitmapPlotter.h"
+#include "PerPixelBitmapPlotter.h"
+#include "SupersampledBitmapPlotter.h"
+#include "LfpChannelColourScheme.h"
+
+#include
+
+using namespace LfpViewer;
+
+#pragma mark - EventDisplayInterface -
+// Event display Options --------------------------------------------------------------------
+
+EventDisplayInterface::EventDisplayInterface(LfpDisplay* display_, LfpDisplayCanvas* canvas_, int chNum):
+ isEnabled(true), display(display_), canvas(canvas_)
+{
+
+ channelNumber = chNum;
+
+ chButton = new UtilityButton(String(channelNumber+1), Font("Small Text", 13, Font::plain));
+ chButton->setRadius(5.0f);
+ chButton->setBounds(4,4,14,14);
+ chButton->setEnabledState(true);
+ chButton->setCorners(true, false, true, false);
+ //chButton.color = display->channelColours[channelNumber*2];
+ chButton->addListener(this);
+ addAndMakeVisible(chButton);
+
+ checkEnabledState();
+
+}
+
+EventDisplayInterface::~EventDisplayInterface()
+{
+
+}
+
+void EventDisplayInterface::checkEnabledState()
+{
+ isEnabled = display->getEventDisplayState(channelNumber);
+
+ //repaint();
+}
+
+void EventDisplayInterface::buttonClicked(Button* button)
+{
+ checkEnabledState();
+ if (isEnabled)
+ {
+ display->setEventDisplayState(channelNumber, false);
+ }
+ else
+ {
+ display->setEventDisplayState(channelNumber, true);
+ }
+
+ repaint();
+
+}
+
+void EventDisplayInterface::paint(Graphics& g)
+{
+
+ checkEnabledState();
+
+ if (isEnabled)
+ {
+ g.setColour(display->channelColours[channelNumber*2]);
+ g.fillRoundedRectangle(2,2,18,18,6.0f);
+ }
+
+ //g.drawText(String(channelNumber), 8, 2, 200, 15, Justification::left, false);
+
+}
+
diff --git a/Plugins/LfpDisplayNode/EventDisplayInterface.h b/Plugins/LfpDisplayNode/EventDisplayInterface.h
new file mode 100644
index 0000000000..047b3a9d29
--- /dev/null
+++ b/Plugins/LfpDisplayNode/EventDisplayInterface.h
@@ -0,0 +1,66 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __EVENTDISPLAYINTERFACE_H__
+#define __EVENTDISPLAYINTERFACE_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+namespace LfpViewer {
+#pragma mark - EventDisplayInterface -
+//==============================================================================
+/**
+ Interface class for Event Display channels.
+ */
+class EventDisplayInterface : public Component,
+ public Button::Listener
+{
+public:
+ EventDisplayInterface(LfpDisplay*, LfpDisplayCanvas*, int chNum);
+ ~EventDisplayInterface();
+
+ void paint(Graphics& g);
+
+ void buttonClicked(Button* button);
+
+ void checkEnabledState();
+
+ bool isEnabled;
+
+private:
+
+ int channelNumber;
+
+ LfpDisplay* display;
+ LfpDisplayCanvas* canvas;
+
+ ScopedPointer chButton;
+
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpBitmapPlotter.h b/Plugins/LfpDisplayNode/LfpBitmapPlotter.h
new file mode 100644
index 0000000000..1db48ffb03
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpBitmapPlotter.h
@@ -0,0 +1,55 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __LFPBITMAPPLOTTER_H__
+#define __LFPBITMAPPLOTTER_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+namespace LfpViewer {
+#pragma mark - LfpBitmapPlotter -
+//==============================================================================
+/**
+ Interface class for different plotting methods.
+ */
+class LfpBitmapPlotter
+{
+public:
+ LfpBitmapPlotter(LfpDisplay * lfpDisplay)
+ : display(lfpDisplay)
+ {}
+ virtual ~LfpBitmapPlotter() {}
+
+ /** Plots one subsample of data from a single channel to the bitmap provided */
+ virtual void plot(Image::BitmapData &bitmapData, LfpBitmapPlotterInfo &plotterInfo) = 0;
+
+protected:
+ LfpDisplay * display;
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpBitmapPlotterInfo.h b/Plugins/LfpDisplayNode/LfpBitmapPlotterInfo.h
new file mode 100644
index 0000000000..c0030547c6
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpBitmapPlotterInfo.h
@@ -0,0 +1,61 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __LFPBITMAPPLOTTERINFO_H__
+#define __LFPBITMAPPLOTTERINFO_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+namespace LfpViewer {
+#pragma mark - LfpBitmapPlotterInfo -
+//==============================================================================
+/**
+ Information struct for plotting method encapsulation classes.
+ */
+struct LfpBitmapPlotterInfo
+{
+ int channelID;
+ int samp;
+ int to;
+ int from;
+ int x;
+ int y;
+ int height;
+ int width;
+ float channelHeightFloat;
+ std::array samplesPerPixel;
+ int sampleCountPerPixel;
+ float range;
+ int samplerange;
+ float histogramParameterA;
+ Colour lineColour;
+ Colour lineColourBright;
+ Colour lineColourDark;
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpChannelColourScheme.cpp b/Plugins/LfpDisplayNode/LfpChannelColourScheme.cpp
new file mode 100644
index 0000000000..5ecb09704a
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpChannelColourScheme.cpp
@@ -0,0 +1,57 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "LfpChannelColourScheme.h"
+#include "LfpDisplayNode.h"
+#include "LfpDisplayCanvas.h"
+#include "ShowHideOptionsButton.h"
+#include "LfpDisplayOptions.h"
+#include "LfpTimescale.h"
+#include "LfpDisplay.h"
+#include "LfpChannelDisplay.h"
+#include "LfpChannelDisplayInfo.h"
+#include "EventDisplayInterface.h"
+#include "LfpViewport.h"
+#include "LfpBitmapPlotterInfo.h"
+#include "LfpBitmapPlotter.h"
+#include "PerPixelBitmapPlotter.h"
+#include "SupersampledBitmapPlotter.h"
+
+#include
+
+using namespace LfpViewer;
+
+#pragma mark - LfpChannelColourScheme -
+
+int LfpChannelColourScheme::colourGrouping = 1;
+
+void LfpChannelColourScheme::setColourGrouping(int grouping)
+{
+ colourGrouping = grouping;
+}
+
+int LfpChannelColourScheme::getColourGrouping()
+{
+ return colourGrouping;
+}
+
diff --git a/Plugins/LfpDisplayNode/LfpChannelColourScheme.h b/Plugins/LfpDisplayNode/LfpChannelColourScheme.h
new file mode 100644
index 0000000000..539af16d06
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpChannelColourScheme.h
@@ -0,0 +1,71 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __LFPCHANNELCOLOURSCHEME_H__
+#define __LFPCHANNELCOLOURSCHEME_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+namespace LfpViewer {
+#pragma mark - LfpChannelColourScheme -
+/**
+ Interface for a color scheme object
+ */
+class LfpChannelColourScheme : public Component
+{
+public:
+ LfpChannelColourScheme(int numColourChannels_, LfpDisplay* display, LfpDisplayCanvas* canvas)
+ : lfpDisplay(display)
+ , canvas(canvas)
+ , numColourChannels(numColourChannels_)
+ { }
+
+ virtual ~LfpChannelColourScheme() {}
+
+ void paint(Graphics &g) override {}
+ void resized() override {}
+
+ virtual const Colour getColourForIndex(int index) const = 0;
+
+ /** Returns true if a color scheme has configurable UI elements that
+ must be drawn to the options drawer. Subclasses should override this
+ if they have drawable elements in the options drawer. */
+ virtual bool hasConfigurableElements() { return false; }
+
+ void setColourGrouping(int grouping);
+ int getColourGrouping();
+
+protected:
+ LfpDisplay * lfpDisplay;
+ LfpDisplayCanvas * canvas;
+
+ int numColourChannels;
+ static int colourGrouping;
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpChannelDisplay.cpp b/Plugins/LfpDisplayNode/LfpChannelDisplay.cpp
new file mode 100644
index 0000000000..af878ee2b0
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpChannelDisplay.cpp
@@ -0,0 +1,516 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "LfpChannelDisplay.h"
+#include "LfpDisplayNode.h"
+#include "LfpDisplayCanvas.h"
+#include "ShowHideOptionsButton.h"
+#include "LfpDisplayOptions.h"
+#include "LfpTimescale.h"
+#include "LfpDisplay.h"
+#include "LfpChannelDisplayInfo.h"
+#include "EventDisplayInterface.h"
+#include "LfpViewport.h"
+#include "LfpBitmapPlotterInfo.h"
+#include "LfpBitmapPlotter.h"
+#include "PerPixelBitmapPlotter.h"
+#include "SupersampledBitmapPlotter.h"
+#include "LfpChannelColourScheme.h"
+
+#include
+
+using namespace LfpViewer;
+
+#pragma mark - LfpChannelDisplay -
+// ------------------------------------------------------------------
+
+LfpChannelDisplay::LfpChannelDisplay(LfpDisplayCanvas* c, LfpDisplay* d, LfpDisplayOptions* o, int channelNumber)
+ : canvas(c)
+ , display(d)
+ , options(o)
+ , isSelected(false)
+ , chan(channelNumber)
+ , name("")
+ , drawableChan(channelNumber)
+ , channelOverlap(300)
+ , channelHeight(30)
+ , range(250.0f)
+ , isEnabled(true)
+ , inputInverted(false)
+ , canBeInverted(true)
+ , drawMethod(false)
+ , isHidden(false)
+{
+
+ name = String(channelNumber+1); // default is to make the channelNumber the name
+
+ channelHeightFloat = (float) channelHeight;
+
+ channelFont = Font("Default", channelHeight*0.6, Font::plain);
+
+ lineColour = Colour(255,255,255);
+
+ type = options->getChannelType(channelNumber);
+ typeStr = options->getTypeName(type);
+
+}
+
+LfpChannelDisplay::~LfpChannelDisplay()
+{
+
+}
+
+void LfpChannelDisplay::resized()
+{
+ // all of this will likely need to be moved into the sharedLfpDisplay image in the lfpDisplay, not here
+ // now that the complete height is know, the sharedLfpDisplay image that we'll draw the pixel-wise lfp plot to needs to be resized
+ //lfpChannelBitmap = Image(Image::ARGB, getWidth(), getHeight(), false);
+}
+
+void LfpChannelDisplay::updateType()
+{
+ type = options->getChannelType(chan);
+ typeStr = options->getTypeName(type);
+}
+
+void LfpChannelDisplay::setEnabledState(bool state)
+{
+
+ //if (state)
+ //std::cout << "Setting channel " << name << " to true." << std::endl;
+ //else
+ //std::cout << "Setting channel " << name << " to false." << std::endl;
+
+ isEnabled = state;
+
+}
+
+void LfpChannelDisplay::setHidden(bool isHidden_)
+{
+ isHidden = isHidden_;
+ isEnabled = !isHidden;
+}
+
+void LfpChannelDisplay::pxPaint()
+{
+ if (!isEnabled) return; // return early if THIS display is not enabled
+
+ Image::BitmapData bdLfpChannelBitmap(display->lfpChannelBitmap, 0,0, display->lfpChannelBitmap.getWidth(), display->lfpChannelBitmap.getHeight());
+
+ int center = getHeight()/2;
+
+ // max and min of channel in absolute px coords for event displays etc - actual data might be drawn outside of this range
+ int jfrom_wholechannel= (int) (getY()+center-channelHeight/2)+1 +0 ;
+ int jto_wholechannel= (int) (getY()+center+channelHeight/2) -0;
+
+ //int jfrom_wholechannel_almost= (int) (getY()+center-channelHeight/3)+1 +0 ; // a bit less tall, for saturation warnings
+ //int jto_wholechannel_almost= (int) (getY()+center+channelHeight/3) -0;
+
+ // max and min of channel, this is the range where actual data is drawn
+ int jfrom_wholechannel_clip= (int) (getY()+center-(channelHeight)*canvas->channelOverlapFactor)+1 ;
+ int jto_wholechannel_clip = (int) (getY()+center+(channelHeight)*canvas->channelOverlapFactor) -0;
+
+ if (jfrom_wholechannel<0) {jfrom_wholechannel=0;};
+ if (jto_wholechannel >= display->lfpChannelBitmap.getHeight()) {jto_wholechannel=display->lfpChannelBitmap.getHeight()-1;};
+
+ // draw most recent drawn sample position
+ if (canvas->screenBufferIndex[chan]+1 <= display->lfpChannelBitmap.getWidth())
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k+=2) // draw line
+ bdLfpChannelBitmap.setPixelColour(canvas->screenBufferIndex[chan]+1,k, Colours::yellow);
+
+ bool clipWarningHi =false; // keep track if something clipped in the display, so we can draw warnings after the data pixels are done
+ bool clipWarningLo =false;
+
+ bool saturateWarningHi =false; // similar, but for saturating the amplifier, not just the display - make this warning very visible
+ bool saturateWarningLo =false;
+
+ // pre compute some colors for later so we dont do it once per pixel.
+ Colour lineColourBright = lineColour.withMultipliedBrightness(2.0f);
+ //Colour lineColourDark = lineColour.withMultipliedSaturation(0.5f).withMultipliedBrightness(0.3f);
+ Colour lineColourDark = lineColour.withMultipliedSaturation(0.5f*canvas->histogramParameterB).withMultipliedBrightness(canvas->histogramParameterB);
+
+ int stepSize = 1;
+ int from = 0; // for vertical line drawing in the LFP data
+ int to = 0;
+
+ int ifrom = canvas->lastScreenBufferIndex[chan] - 1; // need to start drawing a bit before the actual redraw window for the interpolated line to join correctly
+
+ if (ifrom < 0)
+ ifrom = 0;
+
+ int ito = canvas->screenBufferIndex[chan] +0;
+
+ if (fullredraw)
+ {
+ ifrom = 0; //canvas->leftmargin;
+ ito = getWidth()-stepSize;
+ fullredraw = false;
+ }
+
+ bool drawWithOffsetCorrection = display->getMedianOffsetPlotting();
+
+ LfpBitmapPlotterInfo plotterInfo; // hold and pass plotting info for each plotting method class
+
+ for (int i = ifrom; i < ito ; i += stepSize) // redraw only changed portion
+ {
+ if (i < display->lfpChannelBitmap.getWidth())
+ {
+ //draw zero line
+ int m = getY()+center;
+
+ if(m > 0 && m < display->lfpChannelBitmap.getHeight())
+ {
+ if ( bdLfpChannelBitmap.getPixelColour(i,m) == display->backgroundColour ) { // make sure we're not drawing over an existing plot from another channel
+ bdLfpChannelBitmap.setPixelColour(i,m,Colour(50,50,50));
+ }
+ }
+
+ //draw range markers
+ if (isSelected)
+ {
+ int start = getY()+center -channelHeight/2;
+ int jump = channelHeight/4;
+
+ for (m = start; m <= start + jump*4; m += jump)
+ {
+ if (m > 0 && m < display->lfpChannelBitmap.getHeight())
+ {
+ if ( bdLfpChannelBitmap.getPixelColour(i,m) == display->backgroundColour ) // make sure we're not drawing over an existing plot from another channel
+ bdLfpChannelBitmap.setPixelColour(i, m, Colour(80,80,80));
+ }
+ }
+ }
+
+ // draw event markers
+ int rawEventState = canvas->getYCoord(canvas->getNumChannels(), i);// get last channel+1 in buffer (represents events)
+
+ for (int ev_ch = 0; ev_ch < 8 ; ev_ch++) // for all event channels
+ {
+ if (display->getEventDisplayState(ev_ch)) // check if plotting for this channel is enabled
+ {
+ if (rawEventState & (1 << ev_ch)) // events are representet by a bit code, so we have to extract the individual bits with a mask
+ {
+// std::cout << "Drawing event." << std::endl;
+ Colour currentcolor=display->channelColours[ev_ch*2];
+
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++) // draw line
+ bdLfpChannelBitmap.setPixelColour(i,k,bdLfpChannelBitmap.getPixelColour(i,k).interpolatedWith(currentcolor,0.3f));
+
+ }
+ }
+ }
+
+ //std::cout << "e " << canvas->getYCoord(canvas->getNumChannels()-1, i) << std::endl;
+
+ // set max-min range for plotting, used in all methods
+ double a = (canvas->getYCoordMax(chan, i)/range*channelHeightFloat);
+ double b = (canvas->getYCoordMin(chan, i)/range*channelHeightFloat);
+
+ double mean = (canvas->getMean(chan)/range*channelHeightFloat);
+
+ if (drawWithOffsetCorrection)
+ {
+ a -= mean;
+ b -= mean;
+ }
+
+ double a_raw = canvas->getYCoordMax(chan, i);
+ double b_raw = canvas->getYCoordMin(chan, i);
+ double from_raw=0; double to_raw=0;
+
+ //double m = (canvas->getYCoordMean(chan, i)/range*channelHeightFloat)+getHeight()/2;
+ if (achannelOverlapFactor;
+ if (lm>0)
+ lm=-lm;
+
+ if (from > -lm) {from = -lm; clipWarningHi=true;};
+ if (to > -lm) {to = -lm; clipWarningHi=true;};
+ if (from < lm) {from = lm; clipWarningLo=true;};
+ if (to < lm) {to = lm; clipWarningLo=true;};
+
+ // test if raw data is clipped for displaying saturation warning
+ if (from_raw > options->selectedSaturationValueFloat) { saturateWarningHi=true;};
+ if (to_raw > options->selectedSaturationValueFloat) { saturateWarningHi=true;};
+ if (from_raw < -options->selectedSaturationValueFloat) { saturateWarningLo=true;};
+ if (to_raw < -options->selectedSaturationValueFloat) { saturateWarningLo=true;};
+
+ bool spikeFlag = display->getSpikeRasterPlotting()
+ && !(saturateWarningHi || saturateWarningLo)
+ && (from_raw - canvas->getYCoordMean(chan, i) < display->getSpikeRasterThreshold()
+ || to_raw - canvas->getYCoordMean(chan, i) < display->getSpikeRasterThreshold());
+
+ from = from + getHeight()/2; // so the plot is centered in the channeldisplay
+ to = to + getHeight()/2;
+
+ int samplerange = to - from;
+
+ if (drawMethod) // switched between 'supersampled' drawing and simple pixel wise drawing
+ { // histogram based supersampling method
+ plotterInfo.channelID = chan;
+ plotterInfo.samp = i;
+ plotterInfo.y = getY();
+ plotterInfo.from = from;
+ plotterInfo.height = getHeight();
+ plotterInfo.lineColourBright = lineColourBright;
+ plotterInfo.lineColourDark = lineColourDark;
+ plotterInfo.range = range;
+ plotterInfo.channelHeightFloat = channelHeightFloat;
+ plotterInfo.sampleCountPerPixel = canvas->getSampleCountPerPixel(i);
+ plotterInfo.samplesPerPixel = canvas->getSamplesPerPixel(chan, i);
+ plotterInfo.histogramParameterA = canvas->histogramParameterA;
+ plotterInfo.samplerange = samplerange;
+
+ // TODO: (kelly) complete transition toward plotter class encapsulation
+// display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo);
+
+ }
+ else //drawmethod
+ { // simple per-pixel min-max drawing, has no anti-aliasing, but runs faster
+
+ plotterInfo.channelID = chan;
+ plotterInfo.y = getY();
+ plotterInfo.from = from;
+ plotterInfo.to = to;
+ plotterInfo.samp = i;
+ plotterInfo.lineColour = lineColour;
+
+ // TODO: (kelly) complete transition toward plotter class encapsulation
+// display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo); // plotterInfo is prepared above
+
+ }
+
+ // Do the actual plotting for the selected plotting method
+ if (!display->getSpikeRasterPlotting())
+ display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo);
+
+ // now draw warnings, if needed
+ if (canvas->drawClipWarning) // draw simple warning if display cuts off data
+ {
+
+ if(clipWarningHi) {
+ for (int j=0; j<=3; j++)
+ {
+ int clipmarker = jto_wholechannel_clip;
+
+ if(clipmarker>0 && clipmarkerlfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,clipmarker-j,Colour(255,255,255));
+ }
+ }
+ }
+
+ if(clipWarningLo) {
+ for (int j=0; j<=3; j++)
+ {
+ int clipmarker = jfrom_wholechannel_clip;
+
+ if(clipmarker>0 && clipmarkerlfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,clipmarker+j,Colour(255,255,255));
+ }
+ }
+ }
+
+ clipWarningHi=false;
+ clipWarningLo=false;
+ }
+
+ if (spikeFlag) // draw spikes
+ {
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++){ // draw line
+ if(k>0 && klfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,k,lineColour);
+ }
+ };
+ }
+
+ if (canvas->drawSaturationWarning) // draw bigger warning if actual data gets cuts off
+ {
+
+ if(saturateWarningHi || saturateWarningLo) {
+
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++){ // draw line
+ Colour thiscolour=Colour(255,0,0);
+ if (fmod((i+k),50)>25){
+ thiscolour=Colour(255,255,255);
+ }
+ if(k>0 && klfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,k,thiscolour);
+ }
+ };
+ }
+
+ saturateWarningHi=false; // we likely just need one of this because for this warning we dont care if its saturating on the positive or negative side
+ saturateWarningLo=false;
+ }
+ } // if i < getWidth()
+
+ } // for i (x pixels)
+
+}
+
+void LfpChannelDisplay::paint(Graphics& g) {}
+
+PopupMenu LfpChannelDisplay::getOptions()
+{
+
+ PopupMenu menu;
+ menu.addItem(1, "Invert signal", true, inputInverted);
+
+ return menu;
+}
+
+void LfpChannelDisplay::changeParameter(int id)
+{
+ switch (id)
+ {
+ case 1:
+ setInputInverted(!inputInverted);
+ default:
+ break;
+ }
+}
+
+void LfpChannelDisplay::setRange(float r)
+{
+
+ range = r;
+
+ //std::cout << "Range: " << r << std::endl;
+}
+
+int LfpChannelDisplay::getRange()
+{
+ return range;
+}
+
+void LfpChannelDisplay::select()
+{
+ isSelected = true;
+}
+
+void LfpChannelDisplay::deselect()
+{
+ isSelected = false;
+}
+
+bool LfpChannelDisplay::getSelected()
+{
+ return isSelected;
+}
+
+void LfpChannelDisplay::setColour(Colour c)
+{
+ lineColour = c;
+}
+
+void LfpChannelDisplay::setChannelHeight(int c)
+{
+ channelHeight = c;
+
+ channelHeightFloat = (float) channelHeight;
+
+ if (!inputInverted)
+ channelHeightFloat = -channelHeightFloat;
+
+ channelOverlap = channelHeight*2;
+}
+
+int LfpChannelDisplay::getChannelHeight()
+{
+
+ return channelHeight;
+}
+
+void LfpChannelDisplay::setChannelOverlap(int overlap)
+{
+ channelOverlap = overlap;
+}
+
+int LfpChannelDisplay::getChannelOverlap()
+{
+ return channelOverlap;
+}
+
+int LfpChannelDisplay::getChannelNumber()
+{
+ return chan;
+}
+
+String LfpChannelDisplay::getName()
+{
+ return name;
+}
+
+int LfpChannelDisplay::getDrawableChannelNumber()
+{
+ return drawableChan;
+}
+
+void LfpChannelDisplay::setDrawableChannelNumber(int channelId)
+{
+ drawableChan = channelId;
+}
+
+void LfpChannelDisplay::setCanBeInverted(bool _canBeInverted)
+{
+ canBeInverted = _canBeInverted;
+}
+
+void LfpChannelDisplay::setInputInverted(bool isInverted)
+{
+ if (canBeInverted)
+ {
+ inputInverted = isInverted;
+ setChannelHeight(channelHeight);
+ }
+}
+
+void LfpChannelDisplay::setDrawMethod(bool isDrawMethod)
+{
+
+ drawMethod = isDrawMethod;
+
+}
+
+void LfpChannelDisplay::setName(String name_)
+{
+ name = name_;
+}
+
+DataChannel::DataChannelTypes LfpChannelDisplay::getType()
+{
+ return type;
+}
+
diff --git a/Plugins/LfpDisplayNode/LfpChannelDisplay.h b/Plugins/LfpDisplayNode/LfpChannelDisplay.h
new file mode 100644
index 0000000000..8fc848e11c
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpChannelDisplay.h
@@ -0,0 +1,149 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __LFPCHANNELDISPLAY_H__
+#define __LFPCHANNELDISPLAY_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+namespace LfpViewer {
+#pragma mark - LfpChannelDisplay -
+//==============================================================================
+/**
+ Displays the information pertaining to a single data channel.
+ */
+class LfpChannelDisplay : public Component
+{
+public:
+ LfpChannelDisplay(LfpDisplayCanvas*, LfpDisplay*, LfpDisplayOptions*, int channelNumber);
+ ~LfpChannelDisplay();
+
+ void resized();
+
+ void paint(Graphics& g);
+
+ void pxPaint(); // like paint, but just populate lfpChannelBitmap
+ // needs to avoid a paint(Graphics& g) mechanism here becauswe we need to clear the screen in the lfpDisplay repaint(),
+ // because otherwise we cant deal with the channel overlap (need to clear a vertical section first, _then_ all channels are dawn, so cant do it per channel)
+
+ void select();
+ void deselect();
+
+ bool getSelected();
+
+ void setName(String);
+
+ void setColour(Colour c);
+
+ void setChannelHeight(int);
+ int getChannelHeight();
+
+ void setChannelOverlap(int);
+ int getChannelOverlap();
+
+ /** Return the assigned channel number */
+ int getChannelNumber();
+
+ /** Return the assigned channel name */
+ String getName();
+
+ /** Returns the assigned channel number for this display, relative
+ to the subset of channels being drawn to the canvas */
+ int getDrawableChannelNumber();
+
+ /** Set the channel number of this channel relative to the subset of
+ channels being drawn to the canvas */
+ void setDrawableChannelNumber(int channelId);
+
+ void setRange(float range);
+ int getRange();
+
+ void setInputInverted(bool);
+ void setCanBeInverted(bool);
+
+ void setDrawMethod(bool);
+
+ PopupMenu getOptions();
+ void changeParameter(const int id);
+
+ void setEnabledState(bool);
+ bool getEnabledState()
+ {
+ return isEnabled;
+ }
+
+ /** Set the isHidden flag, indicates whether this channel display
+ should render to screen or not */
+ void setHidden(bool);
+
+ /** Return a bool flag describing whether this channel display is
+ hidden from the canvas */
+ bool getHidden() {
+ return isHidden;
+ }
+
+ DataChannel::DataChannelTypes getType();
+ void updateType();
+
+ bool fullredraw; // used to indicate that a full redraw is required. is set false after each full redraw
+
+protected:
+
+ LfpDisplayCanvas* canvas;
+ LfpDisplay* display;
+ LfpDisplayOptions* options;
+
+ bool isSelected;
+ bool isHidden;
+
+ int chan;
+ int drawableChan;
+
+ String name;
+
+ Font channelFont;
+
+ Colour lineColour;
+
+ int channelOverlap;
+ int channelHeight;
+ float channelHeightFloat;
+
+ float range;
+
+ bool isEnabled;
+ bool inputInverted;
+ bool canBeInverted;
+ bool drawMethod;
+
+ DataChannel::DataChannelTypes type;
+ String typeStr;
+
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpChannelDisplayInfo.cpp b/Plugins/LfpDisplayNode/LfpChannelDisplayInfo.cpp
new file mode 100644
index 0000000000..225cba14fd
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpChannelDisplayInfo.cpp
@@ -0,0 +1,329 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "LfpChannelDisplayInfo.h"
+#include "LfpDisplayNode.h"
+#include "LfpDisplayCanvas.h"
+#include "ShowHideOptionsButton.h"
+#include "LfpDisplayOptions.h"
+#include "LfpTimescale.h"
+#include "LfpDisplay.h"
+#include "LfpChannelDisplay.h"
+#include "EventDisplayInterface.h"
+#include "LfpViewport.h"
+#include "LfpBitmapPlotterInfo.h"
+#include "LfpBitmapPlotter.h"
+#include "PerPixelBitmapPlotter.h"
+#include "SupersampledBitmapPlotter.h"
+#include "LfpChannelColourScheme.h"
+
+#include
+
+using namespace LfpViewer;
+
+#pragma mark - LfpChannelDisplayInfo -
+// -------------------------------
+
+LfpChannelDisplayInfo::LfpChannelDisplayInfo(LfpDisplayCanvas* canvas_, LfpDisplay* display_, LfpDisplayOptions* options_, int ch)
+ : LfpChannelDisplay(canvas_, display_, options_, ch)
+{
+
+ chan = ch;
+ x = -1.0f;
+ y = -1.0f;
+
+// enableButton = new UtilityButton(String(ch+1), Font("Small Text", 13, Font::plain));
+ enableButton = new UtilityButton("", Font("Small Text", 13, Font::plain));
+ enableButton->setRadius(5.0f);
+
+ enableButton->setEnabledState(true);
+ enableButton->setCorners(true, true, true, true);
+ enableButton->addListener(this);
+ enableButton->setClickingTogglesState(true);
+ enableButton->setToggleState(true, dontSendNotification);
+
+ isSingleChannel = false;
+
+ addAndMakeVisible(enableButton);
+
+}
+
+void LfpChannelDisplayInfo::updateType()
+{
+ type = options->getChannelType(chan);
+ typeStr = options->getTypeName(type);
+ repaint();
+}
+
+void LfpChannelDisplayInfo::buttonClicked(Button* button)
+{
+
+ bool state = button->getToggleState();
+
+ display->setEnabledState(state, chan);
+
+ //UtilityButton* b = (UtilityButton*) button;
+
+ // if (state)
+ // {
+ // b->setLabel("ON");
+ // } else {
+ // b->setLabel("OFF");
+ // }
+
+ //std::cout << "Turn channel " << chan << " to " << button->getToggleState() << std::endl;
+
+}
+
+void LfpChannelDisplayInfo::setEnabledState(bool state)
+{
+ enableButton->setToggleState(state, sendNotification);
+}
+
+void LfpChannelDisplayInfo::setSingleChannelState(bool state)
+{
+ isSingleChannel = state;
+}
+
+int LfpChannelDisplayInfo::getChannelSampleRate()
+{
+ return samplerate;
+}
+
+void LfpChannelDisplayInfo::setChannelSampleRate(int samplerate_)
+{
+ samplerate = samplerate_;
+}
+
+void LfpChannelDisplayInfo::mouseDrag(const MouseEvent &e)
+{
+ if (e.mods.isLeftButtonDown()) // double check that we initiate only for left click and hold
+ {
+ if (e.mods.isCommandDown() && !display->getSingleChannelState()) // CTRL + drag -> change channel spacing
+ {
+
+ // init state in our track zooming info struct
+ if (!display->trackZoomInfo.isScrollingY)
+ {
+ auto & zoomInfo = display->trackZoomInfo;
+
+ zoomInfo.isScrollingY = true;
+ zoomInfo.componentStartHeight = getChannelHeight();
+ zoomInfo.zoomPivotRatioY = (getY() + e.getMouseDownY())/(float)display->getHeight();
+ zoomInfo.zoomPivotRatioX = (getX() + e.getMouseDownX())/(float)display->getWidth();
+ zoomInfo.zoomPivotViewportOffset = getPosition() + e.getMouseDownPosition() - canvas->viewport->getViewPosition();
+
+ zoomInfo.unpauseOnScrollEnd = !display->isPaused;
+ if (!display->isPaused) display->options->togglePauseButton(true);
+ }
+
+ int h = display->trackZoomInfo.componentStartHeight;
+ int hdiff=0;
+ int dragDeltaY = -0.1 * (e.getScreenPosition().getY() - e.getMouseDownScreenY()); // invert so drag up -> scale up
+
+// std::cout << dragDeltaY << std::endl;
+ if (dragDeltaY > 0)
+ {
+ hdiff = 2 * dragDeltaY;
+ }
+ else
+ {
+ if (h > 5)
+ hdiff = 2 * dragDeltaY;
+ }
+
+ if (abs(h) > 100) // accelerate scrolling for large ranges
+ hdiff *= 3;
+
+ int newHeight = h+hdiff;
+
+ // constrain the spread resizing to max and min values;
+ if (newHeight < display->trackZoomInfo.minZoomHeight)
+ {
+ newHeight = display->trackZoomInfo.minZoomHeight;
+ }
+ else if (newHeight > display->trackZoomInfo.maxZoomHeight)
+ {
+ newHeight = display->trackZoomInfo.maxZoomHeight;
+ }
+
+ // return early if there is nothing to update
+ if (newHeight == getChannelHeight())
+ {
+ return;
+ }
+
+ // set channel heights for all channel
+// display->setChannelHeight(newHeight);
+ for (int i = 0; i < display->getNumChannels(); ++i)
+ {
+ display->channels[i]->setChannelHeight(newHeight);
+ display->channelInfo[i]->setChannelHeight(newHeight);
+ }
+
+ options->setSpreadSelection(newHeight, false, true); // update combobox
+
+ canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw
+
+ display->setBounds(0,0,display->getWidth()-0, display->getChannelHeight()*display->drawableChannels.size()); // update height so that the scrollbar is correct
+
+ int newViewportY = display->trackZoomInfo.zoomPivotRatioY * display->getHeight() - display->trackZoomInfo.zoomPivotViewportOffset.getY();
+ if (newViewportY < 0) newViewportY = 0; // make sure we don't adjust beyond the edge of the actual view
+
+ canvas->viewport->setViewPosition(0, newViewportY);
+ }
+ }
+}
+
+void LfpChannelDisplayInfo::mouseUp(const MouseEvent &e)
+{
+ if (e.mods.isLeftButtonDown() && display->trackZoomInfo.isScrollingY)
+ {
+ display->trackZoomInfo.isScrollingY = false;
+ if (display->trackZoomInfo.unpauseOnScrollEnd)
+ {
+ display->isPaused = false;
+ display->options->togglePauseButton(false);
+ }
+ }
+}
+
+void LfpChannelDisplayInfo::paint(Graphics& g)
+{
+
+ int center = getHeight()/2 - (isSingleChannel?(75):(0));
+ const bool showChannelNumbers = options->getChannelNameState();
+
+ // Draw the channel numbers
+ g.setColour(Colours::grey);
+ const String channelString = (isChannelNumberHidden() ? ("--") :
+ showChannelNumbers ? String(getChannelNumber() + 1) : getName());
+ bool isCentered = !getEnabledButtonVisibility();
+
+ g.drawText(channelString,
+ showChannelNumbers ? 6 : 2,
+ center-4,
+ getWidth()/2,
+ 10,
+ isCentered ? Justification::centred : Justification::centredLeft,
+ false);
+
+ g.setColour(lineColour);
+ g.fillRect(0, 0, 2, getHeight());
+
+ if (getChannelTypeStringVisibility())
+ {
+ g.setFont(Font("Small Text", 13, Font::plain));
+ g.drawText(typeStr,5,center+10,41,10,Justification::centred,false);
+ }
+ // g.setFont(channelHeightFloat*0.3);
+ g.setFont(Font("Small Text", 11, Font::plain));
+
+ if (isSingleChannel)
+ {
+ g.setColour(Colours::darkgrey);
+ g.drawText("STD:", 5, center+90,41,10,Justification::centred,false);
+ g.drawText("MEAN:", 5, center+40,41,10,Justification::centred,false);
+
+ if (x > 0)
+ {
+ g.drawText("uV:", 5, center+140,41,10,Justification::centred,false);
+ }
+ //g.drawText("Y:", 5, center+200,41,10,Justification::centred,false);
+
+ g.setColour(Colours::grey);
+ g.drawText(String(canvas->getStd(chan)), 5, center+110,41,10,Justification::centred,false);
+ g.drawText(String(canvas->getMean(chan)), 5, center+60,41,10,Justification::centred,false);
+ if (x > 0)
+ {
+ //g.drawText(String(x), 5, center+150,41,10,Justification::centred,false);
+ g.drawText(String(y), 5, center+160,41,10,Justification::centred,false);
+ }
+
+ }
+
+ // g.drawText(name, 10, center-channelHeight/2, 200, channelHeight, Justification::left, false);
+
+}
+
+void LfpChannelDisplayInfo::updateXY(float x_, float y_)
+{
+ x = x_;
+ y = y_;
+}
+
+void LfpChannelDisplayInfo::resized()
+{
+
+ int center = getHeight()/2 - (isSingleChannel?(75):(0));
+ setEnabledButtonVisibility(getHeight() >= 16);
+
+ if (getEnabledButtonVisibility())
+ {
+ enableButton->setBounds(getWidth()/2 - 10, center - 5, 10, 10);
+ }
+
+ setChannelNumberIsHidden(getHeight() < 16 && (getDrawableChannelNumber() + 1) % 10 != 0);
+
+ setChannelTypeStringVisibility(getHeight() > 34);
+}
+
+void LfpChannelDisplayInfo::setEnabledButtonVisibility(bool shouldBeVisible)
+{
+ if (shouldBeVisible)
+ {
+ addAndMakeVisible(enableButton);
+ }
+ else if (enableButton->isVisible())
+ {
+ removeChildComponent(enableButton);
+ enableButton->setVisible(false);
+ }
+
+}
+
+bool LfpChannelDisplayInfo::getEnabledButtonVisibility()
+{
+ return enableButton->isVisible();
+}
+
+void LfpChannelDisplayInfo::setChannelTypeStringVisibility(bool shouldBeVisible)
+{
+ channelTypeStringIsVisible = shouldBeVisible;
+}
+
+bool LfpChannelDisplayInfo::getChannelTypeStringVisibility()
+{
+ return channelTypeStringIsVisible || isSingleChannel;
+}
+
+void LfpChannelDisplayInfo::setChannelNumberIsHidden(bool shouldBeHidden)
+{
+ channelNumberHidden = shouldBeHidden;
+}
+
+bool LfpChannelDisplayInfo::isChannelNumberHidden()
+{
+ return channelNumberHidden;
+}
+
diff --git a/Plugins/LfpDisplayNode/LfpChannelDisplayInfo.h b/Plugins/LfpDisplayNode/LfpChannelDisplayInfo.h
new file mode 100644
index 0000000000..26ed256ae3
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpChannelDisplayInfo.h
@@ -0,0 +1,102 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __LFPCHANNELDISPLAYINFO_H__
+#define __LFPCHANNELDISPLAYINFO_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+#include "LfpChannelDisplay.h"
+namespace LfpViewer {
+#pragma mark - LfpChannelDisplayInfo -
+//==============================================================================
+/**
+ Displays meta data pertaining to an associated channel, such as channel number.
+
+ The enableButton displays the channel number and toggles the drawing of the
+ associated LfpChannelDisplay waveform on or off.
+ */
+class LfpChannelDisplayInfo : public LfpChannelDisplay,
+ public Button::Listener
+{
+ friend class LfpDisplay;
+public:
+ LfpChannelDisplayInfo(LfpDisplayCanvas*, LfpDisplay*, LfpDisplayOptions*, int channelNumber);
+
+ void paint(Graphics& g);
+
+ void buttonClicked(Button* button);
+
+ void resized();
+
+ void setEnabledState(bool);
+ void updateType();
+
+ void updateXY(float, float);
+
+ void setSingleChannelState(bool);
+
+ /** Returns the sample rate associated with this channel */
+ int getChannelSampleRate();
+ /** Sets the sample rate associated with this channel */
+ void setChannelSampleRate(int samplerate);
+
+ int getSubprocessorIdx() { return subProcessorIdx; }
+
+ void setSubprocessorIdx(int subProcessorIdx_) { subProcessorIdx = subProcessorIdx_; }
+
+ /** Updates the parent LfpDisplay that the track vertical zoom should update */
+ virtual void mouseDrag(const MouseEvent &event) override;
+
+ /** Disengages the mouse drag to resize track height */
+ virtual void mouseUp(const MouseEvent &event) override;
+
+private:
+
+ bool isSingleChannel;
+ float x, y;
+
+ int samplerate;
+ int subProcessorIdx;
+
+ ScopedPointer enableButton;
+
+ bool channelTypeStringIsVisible;
+ bool channelNumberHidden;
+
+ void setEnabledButtonVisibility(bool shouldBeVisible);
+ bool getEnabledButtonVisibility();
+
+ void setChannelTypeStringVisibility(bool shouldBeVisible);
+ bool getChannelTypeStringVisibility();
+
+ void setChannelNumberIsHidden(bool shouldBeHidden);
+ bool isChannelNumberHidden();
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpDefaultColourScheme.cpp b/Plugins/LfpDisplayNode/LfpDefaultColourScheme.cpp
new file mode 100644
index 0000000000..1087dc5ea4
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpDefaultColourScheme.cpp
@@ -0,0 +1,89 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "LfpDefaultColourScheme.h"
+#include "LfpDisplayNode.h"
+#include "LfpDisplayCanvas.h"
+#include "ShowHideOptionsButton.h"
+#include "LfpDisplayOptions.h"
+#include "LfpTimescale.h"
+#include "LfpDisplay.h"
+#include "LfpChannelDisplay.h"
+#include "LfpChannelDisplayInfo.h"
+#include "EventDisplayInterface.h"
+#include "LfpViewport.h"
+#include "LfpBitmapPlotterInfo.h"
+#include "LfpBitmapPlotter.h"
+#include "PerPixelBitmapPlotter.h"
+#include "SupersampledBitmapPlotter.h"
+#include "LfpChannelColourScheme.h"
+
+#include
+
+using namespace LfpViewer;
+
+#pragma mark - LfpDefaultColourScheme -
+
+Array LfpDefaultColourScheme::colourList = []() -> Array {
+ Array colours;
+ colours.add(Colour(224,185,36));
+ colours.add(Colour(214,210,182));
+ colours.add(Colour(243,119,33));
+ colours.add(Colour(186,157,168));
+ colours.add(Colour(237,37,36));
+ colours.add(Colour(179,122,79));
+ colours.add(Colour(217,46,171));
+ colours.add(Colour(217, 139,196));
+ colours.add(Colour(101,31,255));
+ colours.add(Colour(141,111,181));
+ colours.add(Colour(48,117,255));
+ colours.add(Colour(184,198,224));
+ colours.add(Colour(116,227,156));
+ colours.add(Colour(150,158,155));
+ colours.add(Colour(82,173,0));
+ colours.add(Colour(125,99,32));
+ return colours;
+}();
+
+LfpDefaultColourScheme::LfpDefaultColourScheme(LfpDisplay* display, LfpDisplayCanvas* canvas)
+ : LfpViewer::LfpChannelColourScheme(LfpDefaultColourScheme::colourList.size(), display, canvas)
+{
+ setName("Default");
+}
+
+void LfpDefaultColourScheme::paint(Graphics &g)
+{
+
+}
+
+void LfpDefaultColourScheme::resized()
+{
+
+}
+
+const Colour LfpDefaultColourScheme::getColourForIndex(int index) const
+{
+// return colourList[index % colourList.size()];
+ return colourList[(int(index/colourGrouping)) % colourList.size()];
+}
+
diff --git a/Plugins/LfpDisplayNode/LfpDefaultColourScheme.h b/Plugins/LfpDisplayNode/LfpDefaultColourScheme.h
new file mode 100644
index 0000000000..2ba8e60c43
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpDefaultColourScheme.h
@@ -0,0 +1,52 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __LFPDEFAULTCOLOURSCHEME_H__
+#define __LFPDEFAULTCOLOURSCHEME_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+#include "LfpChannelColourScheme.h"
+namespace LfpViewer {
+#pragma mark - LfpDefaultColourScheme -
+class LfpDefaultColourScheme : public LfpChannelColourScheme
+{
+public:
+ LfpDefaultColourScheme(LfpDisplay*, LfpDisplayCanvas*);
+ virtual ~LfpDefaultColourScheme() {}
+
+ void paint(Graphics &g) override;
+ void resized() override;
+
+ virtual const Colour getColourForIndex(int index) const override;
+
+private:
+ static Array colourList;
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpDisplay.cpp b/Plugins/LfpDisplayNode/LfpDisplay.cpp
new file mode 100644
index 0000000000..d52f11eb96
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpDisplay.cpp
@@ -0,0 +1,943 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "LfpDisplay.h"
+#include "LfpDisplayNode.h"
+#include "LfpDisplayCanvas.h"
+#include "ShowHideOptionsButton.h"
+#include "LfpDisplayOptions.h"
+#include "LfpTimescale.h"
+#include "LfpChannelDisplay.h"
+#include "LfpChannelDisplayInfo.h"
+#include "EventDisplayInterface.h"
+#include "LfpViewport.h"
+#include "LfpBitmapPlotterInfo.h"
+#include "LfpBitmapPlotter.h"
+#include "PerPixelBitmapPlotter.h"
+#include "SupersampledBitmapPlotter.h"
+#include "LfpChannelColourScheme.h"
+#include "LfpDefaultColourScheme.h"
+#include "LfpMonochromaticColourScheme.h"
+#include "LfpGradientColourScheme.h"
+
+#include
+
+using namespace LfpViewer;
+
+#pragma mark - LfpDisplay -
+// ---------------------------------------------------------------
+
+LfpDisplay::LfpDisplay(LfpDisplayCanvas* c, Viewport* v)
+ : singleChan(-1)
+ , canvas(c)
+ , viewport(v)
+ , channelsReversed(false)
+ , displaySkipAmt(0)
+ , m_SpikeRasterPlottingFlag(false)
+{
+ perPixelPlotter = new PerPixelBitmapPlotter(this);
+ supersampledPlotter = new SupersampledBitmapPlotter(this);
+
+// colorScheme = new LfpDefaultColourScheme();
+ colourSchemeList.add(new LfpDefaultColourScheme(this, canvas));
+ colourSchemeList.add(new LfpMonochromaticColourScheme(this, canvas));
+ colourSchemeList.add(new LfpGradientColourScheme(this, canvas));
+
+ activeColourScheme = 0;
+
+ plotter = perPixelPlotter;
+ m_MedianOffsetPlottingFlag = false;
+
+ totalHeight = 0;
+ colorGrouping=1;
+
+ range[0] = 1000;
+ range[1] = 500;
+ range[2] = 500000;
+
+ addMouseListener(this, true);
+
+ // hue cycle
+ //for (int i = 0; i < 15; i++)
+ //{
+ // channelColours.add(Colour(float(sin((3.14/2)*(float(i)/15))),float(1.0),float(1),float(1.0)));
+ //}
+
+// setBufferedToImage(true); // TODO: (kelly) test
+
+ backgroundColour = Colour(0,18,43);
+
+ //hand-built palette
+ channelColours.add(Colour(224,185,36));
+ channelColours.add(Colour(214,210,182));
+ channelColours.add(Colour(243,119,33));
+ channelColours.add(Colour(186,157,168));
+ channelColours.add(Colour(237,37,36));
+ channelColours.add(Colour(179,122,79));
+ channelColours.add(Colour(217,46,171));
+ channelColours.add(Colour(217, 139,196));
+ channelColours.add(Colour(101,31,255));
+ channelColours.add(Colour(141,111,181));
+ channelColours.add(Colour(48,117,255));
+ channelColours.add(Colour(184,198,224));
+ channelColours.add(Colour(116,227,156));
+ channelColours.add(Colour(150,158,155));
+ channelColours.add(Colour(82,173,0));
+ channelColours.add(Colour(125,99,32));
+
+ isPaused=false;
+
+}
+
+LfpDisplay::~LfpDisplay()
+{
+// deleteAllChildren();
+}
+
+int LfpDisplay::getNumChannels()
+{
+ return numChans;
+}
+
+int LfpDisplay::getColorGrouping()
+{
+ return colorGrouping;
+}
+
+void LfpDisplay::setColorGrouping(int i)
+{
+ colorGrouping=i;
+ getColourSchemePtr()->setColourGrouping(i);
+ setColors(); // so that channel colors get re-assigned
+
+}
+
+LfpChannelColourScheme * LfpDisplay::getColourSchemePtr()
+{
+ return colourSchemeList[activeColourScheme];
+}
+
+void LfpDisplay::setNumChannels(int numChannels)
+{
+ numChans = numChannels;
+
+// deleteAllChildren();
+ removeAllChildren();
+
+ channels.clear();
+ channelInfo.clear();
+ drawableChannels.clear();
+
+ totalHeight = 0;
+ cachedDisplayChannelHeight = canvas->getChannelHeight();
+
+ if (numChans > 0)
+ {
+ for (int i = 0; i < numChans; i++)
+ {
+ //std::cout << "Adding new display for channel " << i << std::endl;
+
+ LfpChannelDisplay* lfpChan = new LfpChannelDisplay(canvas, this, options, i);
+
+ //lfpChan->setColour(channelColours[i % channelColours.size()]);
+ lfpChan->setRange(range[options->getChannelType(i)]);
+ lfpChan->setChannelHeight(canvas->getChannelHeight());
+
+ addAndMakeVisible(lfpChan);
+
+ channels.add(lfpChan);
+
+ LfpChannelDisplayInfo* lfpInfo = new LfpChannelDisplayInfo(canvas, this, options, i);
+
+ //lfpInfo->setColour(channelColours[i % channelColours.size()]);
+ lfpInfo->setRange(range[options->getChannelType(i)]);
+ lfpInfo->setChannelHeight(canvas->getChannelHeight());
+ lfpInfo->setSubprocessorIdx(canvas->getChannelSubprocessorIdx(i));
+
+ addAndMakeVisible(lfpInfo);
+
+ channelInfo.add(lfpInfo);
+
+ drawableChannels.add(LfpChannelTrack{
+ lfpChan,
+ lfpInfo
+ });
+
+ savedChannelState.add(true);
+
+ totalHeight += lfpChan->getChannelHeight();
+
+ }
+
+ }
+
+ setColors();
+
+ std::cout << "TOTAL HEIGHT = " << totalHeight << std::endl;
+
+}
+
+void LfpDisplay::setColors()
+{
+ for (int i = 0; i < drawableChannels.size(); i++)
+ {
+
+// channels[i]->setColour(channelColours[(int(i/colorGrouping)+1) % channelColours.size()]);
+// channelInfo[i]->setColour(channelColours[(int(i/colorGrouping)+1) % channelColours.size()]);
+ drawableChannels[i].channel->setColour(getColourSchemePtr()->getColourForIndex(i));
+ drawableChannels[i].channelInfo->setColour(getColourSchemePtr()->getColourForIndex(i));
+ }
+
+}
+
+void LfpDisplay::setActiveColourSchemeIdx(int index)
+{
+ activeColourScheme = index;
+}
+
+int LfpDisplay::getActiveColourSchemeIdx()
+{
+ return activeColourScheme;
+}
+
+int LfpDisplay::getNumColourSchemes()
+{
+ return colourSchemeList.size();
+}
+
+StringArray LfpDisplay::getColourSchemeNameArray()
+{
+ StringArray nameList;
+ for (auto scheme : colourSchemeList)
+ nameList.add(scheme->getName());
+
+ return nameList;
+}
+
+int LfpDisplay::getTotalHeight()
+{
+ return totalHeight;
+}
+
+void LfpDisplay::resized()
+{
+ int totalHeight = 0;
+
+ for (int i = 0; i < drawableChannels.size(); i++)
+ {
+
+ LfpChannelDisplay* disp = drawableChannels[i].channel;
+
+ if (disp->getHidden()) continue;
+
+ disp->setBounds(canvas->leftmargin,
+ totalHeight-(disp->getChannelOverlap()*canvas->channelOverlapFactor)/2,
+ getWidth(),
+ disp->getChannelHeight()+(disp->getChannelOverlap()*canvas->channelOverlapFactor));
+
+ disp-> resized();
+
+ LfpChannelDisplayInfo* info = drawableChannels[i].channelInfo;
+
+ info->setBounds(0,
+ totalHeight-disp->getChannelHeight() + (disp->getChannelOverlap()*canvas->channelOverlapFactor)/4.0,
+ canvas->leftmargin + 50,
+ disp->getChannelHeight());
+
+ totalHeight += disp->getChannelHeight();
+
+ }
+
+ canvas->fullredraw = true; //issue full redraw
+ if (singleChan != -1)
+ viewport->setViewPosition(Point(0,singleChan*getChannelHeight()));
+
+ lfpChannelBitmap = Image(Image::ARGB, getWidth(), getHeight(), false);
+
+ //inititalize black background
+ Graphics gLfpChannelBitmap(lfpChannelBitmap);
+ gLfpChannelBitmap.setColour(Colour(0,0,0)); //background color
+ gLfpChannelBitmap.fillRect(0,0, getWidth(), getHeight());
+
+ canvas->fullredraw = true;
+
+ refresh();
+ // std::cout << "Total height: " << totalHeight << std::endl;
+
+}
+
+void LfpDisplay::paint(Graphics& g)
+{
+
+ g.drawImageAt(lfpChannelBitmap, canvas->leftmargin,0);
+
+}
+
+void LfpDisplay::refresh()
+{
+ // Ensure the lfpChannelBitmap has been initialized
+ if (lfpChannelBitmap.isNull())
+ {
+ resized();
+ }
+
+ // X-bounds of this update
+ int fillfrom = canvas->lastScreenBufferIndex[0];
+ int fillto = (canvas->screenBufferIndex[0]);
+
+ if (fillfrom<0){fillfrom=0;};
+ if (fillto>lfpChannelBitmap.getWidth()){fillto=lfpChannelBitmap.getWidth();};
+
+ int topBorder = viewport->getViewPositionY();
+ int bottomBorder = viewport->getViewHeight() + topBorder;
+
+ // clear appropriate section of the bitmap --
+ // we need to do this before each channel draws its new section of data into lfpChannelBitmap
+ Graphics gLfpChannelBitmap(lfpChannelBitmap);
+ gLfpChannelBitmap.setColour(backgroundColour); //background color
+
+ if (canvas->fullredraw)
+ {
+ gLfpChannelBitmap.fillRect(0,0, getWidth(), getHeight());
+ } else {
+ gLfpChannelBitmap.setColour(backgroundColour); //background color
+
+ gLfpChannelBitmap.fillRect(fillfrom,0, (fillto-fillfrom)+1, getHeight());
+ };
+
+ for (int i = 0; i < numChans; i++)
+// for (int i = 0; i < drawableChannels.size(); ++i)
+ {
+
+ int componentTop = channels[i]->getY();
+ int componentBottom = channels[i]->getHeight() + componentTop;
+
+ if ((topBorder <= componentBottom && bottomBorder >= componentTop)) // only draw things that are visible
+ {
+ if (canvas->fullredraw)
+ {
+ channels[i]->fullredraw = true;
+
+ channels[i]->pxPaint();
+ channelInfo[i]->repaint();
+
+ }
+ else
+ {
+ channels[i]->pxPaint(); // draws to lfpChannelBitmap
+
+ // it's not clear why, but apparently because the pxPaint() in a child component of LfpDisplay, we also need to issue repaint() calls for each channel, even though there's nothin to repaint there. Otherwise, the repaint call in LfpDisplay::refresh(), a few lines down, lags behind the update line by ~60 px. This could ahev something to do with teh reopaint message passing in juce. In any case, this seemingly redundant repaint here seems to fix the issue.
+
+ // we redraw from 0 to +2 (px) relative to the real redraw window, the +1 draws the vertical update line
+ channels[i]->repaint(fillfrom, 0, (fillto-fillfrom)+2, channels[i]->getHeight());
+
+ }
+ //std::cout << i << std::endl;
+ }
+
+ }
+
+ if (fillfrom == 0 && singleChan != -1)
+ {
+ channelInfo[singleChan]->repaint();
+ }
+
+ if (canvas->fullredraw)
+ {
+ repaint(0,topBorder,getWidth(),bottomBorder-topBorder);
+ }else{
+ //repaint(fillfrom, topBorder, (fillto-fillfrom)+1, bottomBorder-topBorder); // doesntb seem to be needed and results in duplicate repaint calls
+ }
+
+ canvas->fullredraw = false;
+}
+
+void LfpDisplay::setRange(float r, DataChannel::DataChannelTypes type)
+{
+ range[type] = r;
+
+ if (channels.size() > 0)
+ {
+
+ for (int i = 0; i < numChans; i++)
+ {
+ if (channels[i]->getType() == type)
+ channels[i]->setRange(range[type]);
+ }
+ canvas->fullredraw = true; //issue full redraw
+ }
+}
+
+int LfpDisplay::getRange()
+{
+ return getRange(options->getSelectedType());
+}
+
+int LfpDisplay::getRange(DataChannel::DataChannelTypes type)
+{
+ for (int i=0; i < numChans; i++)
+ {
+ if (channels[i]->getType() == type)
+ return channels[i]->getRange();
+ }
+ return 0;
+}
+
+void LfpDisplay::setChannelHeight(int r, bool resetSingle)
+{
+ if (!getSingleChannelState()) cachedDisplayChannelHeight = r;
+
+ for (int i = 0; i < numChans; i++)
+ {
+ channels[i]->setChannelHeight(r);
+ channelInfo[i]->setChannelHeight(r);
+ }
+ if (resetSingle && singleChan != -1)
+ {
+ //std::cout << "width " << getWidth() << " numchans " << numChans << " height " << getChannelHeight() << std::endl;
+ setSize(getWidth(),drawableChannels.size()*getChannelHeight());
+ viewport->setScrollBarsShown(true,false);
+ viewport->setViewPosition(Point(0,singleChan*r));
+ singleChan = -1;
+ for (int n = 0; n < numChans; n++)
+ {
+ channelInfo[n]->setEnabledState(savedChannelState[n]);
+ }
+ }
+
+ resized();
+
+}
+
+void LfpDisplay::setInputInverted(bool isInverted)
+{
+
+ for (int i = 0; i < numChans; i++)
+ {
+ channels[i]->setInputInverted(isInverted);
+ }
+
+ resized();
+
+}
+
+void LfpDisplay::setDrawMethod(bool isDrawMethod)
+{
+ for (int i = 0; i < numChans; i++)
+ {
+ channels[i]->setDrawMethod(isDrawMethod);
+ }
+
+ if (isDrawMethod)
+ {
+ plotter = supersampledPlotter;
+ }
+ else
+ {
+ plotter = perPixelPlotter;
+ }
+
+ resized();
+
+}
+
+int LfpDisplay::getChannelHeight()
+{
+// return cachedDisplayChannelHeight;
+ return drawableChannels[0].channel->getChannelHeight();
+// return channels[0]->getChannelHeight();
+}
+
+void LfpDisplay::cacheNewChannelHeight(int r)
+{
+ cachedDisplayChannelHeight = r;
+}
+
+bool LfpDisplay::getChannelsReversed()
+{
+ return channelsReversed;
+}
+
+void LfpDisplay::setChannelsReversed(bool state)
+{
+ if (state == channelsReversed) return; // bail early, in case bookkeeping error
+
+ channelsReversed = state;
+
+ if (getSingleChannelState()) return; // don't reverse if single channel
+
+ // reverse channels that are currently in drawableChannels
+ for (int i = 0, j = drawableChannels.size() - 1, len = drawableChannels.size()/2;
+ i < len;
+ i++, j--)
+ {
+ // remove channel and info components from front and back
+ // moving toward middle
+ removeChildComponent(drawableChannels[i].channel);
+ removeChildComponent(drawableChannels[j].channel);
+ removeChildComponent(drawableChannels[i].channelInfo);
+ removeChildComponent(drawableChannels[j].channelInfo);
+
+ // swap front and back, moving towards middle
+ drawableChannels.swap(i, j);
+
+ // also swap coords
+ {
+ const auto channelBoundsA = drawableChannels[i].channel->getBounds();
+ const auto channelInfoBoundsA = drawableChannels[i].channelInfo->getBounds();
+
+ drawableChannels[i].channel->setBounds(drawableChannels[j].channel->getBounds());
+ drawableChannels[i].channelInfo->setBounds(drawableChannels[j].channelInfo->getBounds());
+ drawableChannels[j].channel->setBounds(channelBoundsA);
+ drawableChannels[j].channelInfo->setBounds(channelInfoBoundsA);
+ }
+ }
+
+ // remove middle component if odd number of channels
+ if (drawableChannels.size() % 2 != 0)
+ {
+ removeChildComponent(drawableChannels[drawableChannels.size()/2+1].channel);
+ removeChildComponent(drawableChannels[drawableChannels.size()/2+1].channelInfo);
+ }
+
+ // add the channels and channel info again
+ for (int i = 0, len = drawableChannels.size(); i < len; i++)
+ {
+
+ if (!drawableChannels[i].channel->getHidden())
+ {
+ addAndMakeVisible(drawableChannels[i].channel);
+ addAndMakeVisible(drawableChannels[i].channelInfo);
+ }
+
+ // flag this to update the waveforms
+ drawableChannels[i].channel->fullredraw = true;
+ }
+
+ // necessary to overwrite lfpChannelBitmap's display
+ refresh();
+}
+
+int LfpDisplay::getChannelDisplaySkipAmount()
+{
+ return displaySkipAmt;
+}
+
+void LfpDisplay::setChannelDisplaySkipAmount(int skipAmt)
+{
+ displaySkipAmt = skipAmt;
+
+ if (!getSingleChannelState())
+ rebuildDrawableChannelsList();
+
+ canvas->redraw();
+}
+
+bool LfpDisplay::getMedianOffsetPlotting()
+{
+ return m_MedianOffsetPlottingFlag;
+}
+
+void LfpDisplay::setMedianOffsetPlotting(bool isEnabled)
+{
+ m_MedianOffsetPlottingFlag = isEnabled;
+}
+
+bool LfpDisplay::getSpikeRasterPlotting()
+{
+ return m_SpikeRasterPlottingFlag;
+}
+
+void LfpDisplay::setSpikeRasterPlotting(bool isEnabled)
+{
+ m_SpikeRasterPlottingFlag = isEnabled;
+}
+
+float LfpDisplay::getSpikeRasterThreshold()
+{
+ return m_SpikeRasterThreshold;
+}
+
+void LfpDisplay::setSpikeRasterThreshold(float thresh)
+{
+ m_SpikeRasterThreshold = thresh;
+}
+
+void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel)
+{
+
+ //std::cout << "Mouse wheel " << e.mods.isCommandDown() << " " << wheel.deltaY << std::endl;
+ //TODO Changing ranges with the wheel is currently broken. With multiple ranges, most
+ //of the wheel range code needs updating
+
+ if (e.mods.isCommandDown() && singleChan == -1) // CTRL + scroll wheel -> change channel spacing
+ {
+ int h = getChannelHeight();
+ int hdiff=0;
+
+ // std::cout << wheel.deltaY << std::endl;
+
+ if (wheel.deltaY > 0)
+ {
+ hdiff = 2;
+ }
+ else
+ {
+ if (h > 5)
+ hdiff = -2;
+ }
+
+ if (abs(h) > 100) // accelerate scrolling for large ranges
+ hdiff *= 3;
+
+ int newHeight = h+hdiff;
+
+ // constrain the spread resizing to max and min values;
+ if (newHeight < trackZoomInfo.minZoomHeight)
+ {
+ newHeight = trackZoomInfo.minZoomHeight;
+ hdiff = 0;
+ }
+ else if (newHeight > trackZoomInfo.maxZoomHeight)
+ {
+ newHeight = trackZoomInfo.maxZoomHeight;
+ hdiff = 0;
+ }
+
+ setChannelHeight(newHeight);
+ int oldX=viewport->getViewPositionX();
+ int oldY=viewport->getViewPositionY();
+
+ setBounds(0,0,getWidth()-0, getChannelHeight()*drawableChannels.size()); // update height so that the scrollbar is correct
+
+ int mouseY=e.getMouseDownY(); // should be y pos relative to inner viewport (0,0)
+ int scrollBy = (mouseY/h)*hdiff*2;// compensate for motion of point under current mouse position
+ viewport->setViewPosition(oldX,oldY+scrollBy); // set back to previous position plus offset
+
+ options->setSpreadSelection(newHeight); // update combobox
+
+ canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw
+ }
+ else
+ {
+ if (e.mods.isAltDown()) // ALT + scroll wheel -> change channel range (was SHIFT but that clamps wheel.deltaY to 0 on OSX for some reason..)
+ {
+ int h = getRange();
+
+ int step = options->getRangeStep(options->getSelectedType());
+
+ // std::cout << wheel.deltaY << std::endl;
+
+ if (wheel.deltaY > 0)
+ {
+ setRange(h+step,options->getSelectedType());
+ }
+ else
+ {
+ if (h > step+1)
+ setRange(h-step,options->getSelectedType());
+ }
+
+ options->setRangeSelection(h); // update combobox
+ canvas->fullredraw = true; //issue full redraw - scrolling without modifier doesnt require a full redraw
+
+ }
+ else // just scroll
+ {
+ // passes the event up to the viewport so the screen scrolls
+ if (viewport != nullptr && e.eventComponent == this) // passes only if it's not a listening event
+ viewport->mouseWheelMove(e.getEventRelativeTo(canvas), wheel);
+
+ }
+
+ }
+ //refresh(); // doesn't seem to be needed now that channels daraw to bitmap
+
+}
+
+void LfpDisplay::toggleSingleChannel(int chan)
+{
+ if (!getSingleChannelState())
+ {
+
+ std::cout << "Single channel on (" << chan << ")" << std::endl;
+ singleChan = chan;
+
+ int newHeight = viewport->getHeight();
+ LfpChannelTrack lfpChannelTrack{drawableChannels[chan].channel, drawableChannels[chan].channelInfo};
+ lfpChannelTrack.channelInfo->setEnabledState(true);
+ lfpChannelTrack.channelInfo->setSingleChannelState(true);
+
+ removeAllChildren();
+
+ // disable unused channels
+ for (int i = 0; i < getNumChannels(); i++)
+ {
+ if (i != chan)
+ {
+ drawableChannels[i].channel->setEnabledState(false);
+ }
+ }
+
+ // update drawableChannels, give only the single channel to focus on
+ drawableChannels.clearQuick();
+ drawableChannels.add(lfpChannelTrack);
+
+ addAndMakeVisible(lfpChannelTrack.channel);
+ addAndMakeVisible(lfpChannelTrack.channelInfo);
+
+ // set channel height and position (so that we allocate the smallest
+ // necessary image size for drawing)
+ setChannelHeight(newHeight, false);
+
+ lfpChannelTrack.channel->setTopLeftPosition(canvas->leftmargin, 0);
+ lfpChannelTrack.channelInfo->setTopLeftPosition(0, 0);
+ setSize(getWidth(), getChannelHeight());
+
+ viewport->setViewPosition(0, 0);
+
+ }
+// else if (chan == singleChan || chan == -2)
+ else
+ {
+ std::cout << "Single channel off" << std::endl;
+ for (int n = 0; n < numChans; n++)
+ {
+ channelInfo[n]->setSingleChannelState(false);
+ }
+
+ setChannelHeight(cachedDisplayChannelHeight);
+
+ reactivateChannels();
+ rebuildDrawableChannelsList();
+ }
+}
+
+void LfpDisplay::reactivateChannels()
+{
+
+ for (int n = 0; n < channels.size(); n++)
+ setEnabledState(savedChannelState[n], n);
+
+}
+
+void LfpDisplay::rebuildDrawableChannelsList()
+{
+
+ if (displaySkipAmt != 0) removeAllChildren(); // start with clean slate
+
+ Array channelsToDraw;
+ drawableChannels = Array();
+
+ // iterate over all channels and select drawable ones
+ for (int i = 0, drawableChannelNum = 0; i < channels.size(); i++)
+ {
+// std::cout << "\tchannel " << i << " has subprocessor index of " << channelInfo[i]->getSubprocessorIdx() << std::endl;
+ // if channel[i] is not sourced from the correct subprocessor, then hide it and continue
+ //if (channelInfo[i]->getSubprocessorIdx() != getDisplayedSubprocessor())
+ //{
+ // channels[i]->setHidden(true);
+ // channelInfo[i]->setHidden(true);
+ // continue;
+ //}
+
+ //std::cout << "Checking for hidden channels" << std::endl;
+ if (displaySkipAmt == 0 || (i % displaySkipAmt == 0)) // no skips, add all channels
+ {
+ channels[i]->setHidden(false);
+ channelInfo[i]->setHidden(false);
+
+ channelInfo[i]->setDrawableChannelNumber(drawableChannelNum++);
+ channelInfo[i]->resized(); // to update the conditional drawing of enableButton and channel num
+
+ channelsToDraw.add(LfpDisplay::LfpChannelTrack{
+ channels[i],
+ channelInfo[i]
+ });
+
+ addAndMakeVisible(channels[i]);
+ addAndMakeVisible(channelInfo[i]);
+ }
+ else // skip some channels
+ {
+// if (i % (displaySkipAmt) == 0) // add these channels
+// {
+// channels[i]->setHidden(false);
+// channelInfo[i]->setHidden(false);
+//
+// channelsToDraw.add(LfpDisplay::LfpChannelTrack{
+// channels[i],
+// channelInfo[i]
+// });
+//
+// addAndMakeVisible(channels[i]);
+// addAndMakeVisible(channelInfo[i]);
+// }
+// else // but not these
+// {
+ channels[i]->setHidden(true);
+ channelInfo[i]->setHidden(true);
+
+ removeChildComponent(channels[i]);
+ removeChildComponent(channelInfo[i]);
+// }
+ }
+ }
+
+ // check if channels should be added to drawableChannels in reverse
+ if (getChannelsReversed())
+ {
+ for (int i = channelsToDraw.size() - 1; i >= 0; --i)
+ {
+ drawableChannels.add(channelsToDraw[i]);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < channelsToDraw.size(); ++i)
+ {
+ drawableChannels.add(channelsToDraw[i]);
+ }
+ }
+
+ // this guards against an exception where the editor sets the drawable samplerate
+ // before the lfpDisplay is fully initialized
+ if (getHeight() > 0 && getWidth() > 0)
+ {
+ canvas->resizeToChannels();
+ }
+
+ setColors();
+}
+
+LfpBitmapPlotter * const LfpDisplay::getPlotterPtr() const
+{
+ return plotter;
+}
+
+bool LfpDisplay::getSingleChannelState()
+{
+ //if (singleChan < 0) return false;
+ //else return true;
+ return singleChan >= 0;
+}
+
+void LfpDisplay::mouseDown(const MouseEvent& event)
+{
+ if (drawableChannels.isEmpty())
+ {
+ return;
+ }
+
+ //int y = event.getMouseDownY(); //relative to each channel pos
+ MouseEvent canvasevent = event.getEventRelativeTo(viewport);
+ int y = canvasevent.getMouseDownY() + viewport->getViewPositionY(); // need to account for scrolling
+ int x = canvasevent.getMouseDownX();
+
+ int dist = 0;
+ int mindist = 10000;
+ int closest = 5;
+ for (int n = 0; n < drawableChannels.size(); n++) // select closest instead of relying on eventComponent
+ {
+ drawableChannels[n].channel->deselect();
+
+ int cpos = (drawableChannels[n].channel->getY() + (drawableChannels[n].channel->getHeight()/2));
+ dist = int(abs(y - cpos));
+
+// std::cout << "Mouse down at " << y << " pos is "<< cpos << " n: " << n << " dist " << dist << std::endl;
+
+ if (dist < mindist)
+ {
+ mindist = dist-1;
+ closest = n;
+ }
+ }
+
+ drawableChannels[closest].channel->select();
+ options->setSelectedType(drawableChannels[closest].channel->getType());
+
+ if (event.mods.isRightButtonDown()) { // if right click
+ PopupMenu channelMenu = channels[closest]->getOptions();
+ const int result = channelMenu.show();
+ drawableChannels[closest].channel->changeParameter(result);
+ }
+ else // if left click
+ {
+// if (singleChan != -1)
+ if (event.getNumberOfClicks() == 2) {
+ toggleSingleChannel(closest);
+ }
+
+ if (getSingleChannelState())
+ {
+
+ // std::cout << "singleChan = " << singleChan << " " << y << " " << drawableChannels[0].channel->getHeight() << " " << getRange() << std::endl;
+ //channelInfo[singleChan]->updateXY(
+ drawableChannels[0].channelInfo->updateXY(
+ float(x)/getWidth()*canvas->timebase,
+ (-(float(y)-viewport->getViewPositionY())/viewport->getViewHeight()*float(getRange()))+float(getRange()/2)
+ );
+ }
+ }
+
+// canvas->fullredraw = true;//issue full redraw
+
+// refresh();
+
+}
+
+bool LfpDisplay::setEventDisplayState(int ch, bool state)
+{
+ eventDisplayEnabled[ch] = state;
+ return eventDisplayEnabled[ch];
+}
+
+bool LfpDisplay::getEventDisplayState(int ch)
+{
+ return eventDisplayEnabled[ch];
+}
+
+void LfpDisplay::setEnabledState(bool state, int chan, bool updateSaved)
+{
+ if (chan < numChans)
+ {
+ channels[chan]->setEnabledState(state);
+ channelInfo[chan]->setEnabledState(state);
+
+ if (updateSaved)
+ savedChannelState.set(chan, state);
+
+ canvas->isChannelEnabled.set(chan, state);
+ }
+}
+
+bool LfpDisplay::getEnabledState(int chan)
+{
+ if (chan < numChans)
+ {
+ return channels[chan]->getEnabledState();
+ }
+
+ return false;
+}
+
diff --git a/Plugins/LfpDisplayNode/LfpDisplay.h b/Plugins/LfpDisplayNode/LfpDisplay.h
new file mode 100644
index 0000000000..c19322eb44
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpDisplay.h
@@ -0,0 +1,236 @@
+/*
+ ------------------------------------------------------------------
+
+ This file is part of the Open Ephys GUI
+ Copyright (C) 2013 Open Ephys
+
+ ------------------------------------------------------------------
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+*/
+#ifndef __LFPDISPLAY_H__
+#define __LFPDISPLAY_H__
+
+#include
+
+#include
+#include
+
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
+namespace LfpViewer {
+#pragma mark - LfpDisplay -
+//==============================================================================
+/**
+
+ Holds and draws all of the LfpDisplayChannel and lfpDisplayChannelInfo
+ instances.
+
+ All of the channels and channelInfos are drawn here to a "master" bitmap
+ lfpChannelBitmap with height equal to the sum of all channel heights. This
+ bitmap is drawn by the LfpViewport using Viewport::setViewedComponent.
+
+ */
+class LfpDisplay : public Component
+{
+public:
+ LfpDisplay(LfpDisplayCanvas*, Viewport*);
+ ~LfpDisplay();
+
+ Image lfpChannelBitmap; // plot as bitmap instead of separately setting pixels
+ // this is done purely for the preformance improvement
+
+ void setNumChannels(int numChannels);
+ int getNumChannels();
+
+ int getTotalHeight();
+
+ void paint(Graphics& g);
+
+ void refresh();
+
+ void resized();
+
+ void reactivateChannels();
+
+ void mouseDown(const MouseEvent& event);
+ void mouseWheelMove(const MouseEvent& event, const MouseWheelDetails& wheel) ;
+
+ void setRange(float range, DataChannel::DataChannelTypes type);
+
+ //Withouth parameters returns selected type
+ int getRange();
+ int getRange(DataChannel::DataChannelTypes type);
+
+ void setChannelHeight(int r, bool resetSingle = true);
+ int getChannelHeight();
+
+ LfpChannelColourScheme * getColourSchemePtr();
+
+ /** Caches a new channel height without updating the channels */
+ void cacheNewChannelHeight(int r);
+
+ void setInputInverted(bool);
+ void setDrawMethod(bool);
+
+ /** Returns a bool indicating if the channels are displayed in reverse order (true) */
+ bool getChannelsReversed();
+
+ /** Reorders the displayed channels, reversed if state == true and normal if false */
+ void setChannelsReversed(bool state);
+
+ /** Returns a factor of 2 by which the displayed channels should skip */
+ int getChannelDisplaySkipAmount();
+
+ /** Set the amount of channels to skip (hide) between each that is displayed */
+ void setChannelDisplaySkipAmount(int skipAmt);
+
+ void setColors();
+
+ void setActiveColourSchemeIdx(int index);
+ int getActiveColourSchemeIdx();
+
+ int getNumColourSchemes();
+ StringArray getColourSchemeNameArray();
+
+ bool setEventDisplayState(int ch, bool state);
+ bool getEventDisplayState(int ch);
+
+ int getColorGrouping();
+ void setColorGrouping(int i);
+
+ void setEnabledState(bool state, int chan, bool updateSavedChans = true);
+ bool getEnabledState(int);
+
+ /** Returns true if the median offset is enabled for plotting, else false */
+ bool getMedianOffsetPlotting();
+
+ /** Sets the state for the median offset plotting function */
+ void setMedianOffsetPlotting(bool isEnabled);
+
+ /** Returns true if spike raster is enabled for plotting, else false */
+ bool getSpikeRasterPlotting();
+
+ /** Sets the state for the spike raster plotting function */
+ void setSpikeRasterPlotting(bool isEnabled);
+
+ /** Returns the value at which the spike raster will detect and draw spikes */
+ float getSpikeRasterThreshold();
+
+ /** Set the threshold value for the spike raster plotting function */
+ void setSpikeRasterThreshold(float thresh);
+
+ /** Returns true if a single channel is focused in viewport */
+ bool getSingleChannelState();
+
+ /** Set the viewport's channel focus behavior.
+
+ When a single channel is selected, it fills the entire viewport and
+ all other channels are hidden. Double clicking a channel's info/event
+ display toggles this setting.
+
+ @param chan If chan is < 0, no channel will be selected for singular
+ focus. Giving a value of 0 or greater hides all channels
+ except for the one at that index in drawableChannels[].
+ Note: this parameter is NOT the index in channel[], but
+ the index of the channel in drawableChannels[].
+ */
+ void toggleSingleChannel(int chan = -2);
+
+ /** Reconstructs the list of drawableChannels based on ordering and filterning parameters */
+ void rebuildDrawableChannelsList();
+
+ /** Returns a const pointer to the internally managed plotter method class */
+ LfpBitmapPlotter * const getPlotterPtr() const;
+
+ Colour backgroundColour;
+
+ Array channelColours;
+
+ OwnedArray channels; // all channels
+ OwnedArray channelInfo; // all channelInfos
+
+ /** Convenience struct for holding a channel and its info in drawableChannels */
+ struct LfpChannelTrack
+ {
+ LfpChannelDisplay * channel;
+ LfpChannelDisplayInfo * channelInfo;
+ };
+ Array drawableChannels; // holds the channels and info that are
+ // drawable to the screen
+
+ bool eventDisplayEnabled[8];
+ bool isPaused; // simple pause function, skips screen buffer updates
+
+ LfpDisplayOptions* options;
+
+ /** Convenience struct to store all variables particular to zooming mechanics */
+ struct TrackZoomInfo_Struct
+ {
+ const int minZoomHeight = 10;
+ const int maxZoomHeight = 150;
+ int currentZoomHeight; // the current zoom height for the drawableChannels (not
+ // currently in use)
+
+ bool isScrollingX = false;
+ bool isScrollingY = false;
+ int componentStartHeight; // a cache for the dimensions of a component during drag events
+ float timescaleStartScale; // a cache for the timescale size during drag events
+ float zoomPivotRatioX; // a cache for calculating the anchor point when adjusting viewport
+ float zoomPivotRatioY;
+ Point zoomPivotViewportOffset; // similar to above, but pixel-wise offset
+ bool unpauseOnScrollEnd;
+ };
+
+ TrackZoomInfo_Struct trackZoomInfo; // and create an instance here
+
+private:
+
+ int singleChan;
+ Array savedChannelState;
+
+ int numChans;
+ int displaySkipAmt;
+ int cachedDisplayChannelHeight; // holds a channel height if reset during single channel focus
+ float drawableSampleRate;
+ uint32 drawableSubprocessor;
+
+ int totalHeight;
+
+ int colorGrouping;
+
+ bool channelsReversed;
+ bool m_MedianOffsetPlottingFlag;
+ bool m_SpikeRasterPlottingFlag;
+ float m_SpikeRasterThreshold;
+
+ LfpDisplayCanvas* canvas;
+ Viewport* viewport;
+
+ float range[3];
+
+ LfpBitmapPlotter * plotter;
+
+ ScopedPointer perPixelPlotter;
+ ScopedPointer supersampledPlotter;
+
+ // TODO: (kelly) add reference to a color scheme
+// LfpChannelColourScheme * colourScheme;
+ uint8 activeColourScheme;
+ OwnedArray colourSchemeList;
+};
+
+}; // namespace
+#endif
diff --git a/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp b/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp
index 5298244e46..1f67716ec1 100644
--- a/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp
+++ b/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp
@@ -22,14 +22,25 @@ along with this program. If not, see .
*/
#include "LfpDisplayCanvas.h"
+#include "LfpDisplayNode.h"
+#include "ShowHideOptionsButton.h"
+#include "LfpDisplayOptions.h"
+#include "LfpTimescale.h"
+#include "LfpDisplay.h"
+#include "LfpChannelDisplay.h"
+#include "LfpChannelDisplayInfo.h"
+#include "EventDisplayInterface.h"
+#include "LfpViewport.h"
+#include "LfpBitmapPlotter.h"
+#include "PerPixelBitmapPlotter.h"
+#include "SupersampledBitmapPlotter.h"
+#include "LfpChannelColourScheme.h"
#include
using namespace LfpViewer;
-
-
-#pragma mark - LfpDisplayCanvas -
+#pragma mark - LfpDisplayCanvas -
LfpDisplayCanvas::LfpDisplayCanvas(LfpDisplayNode* processor_) :
timebase(1.0f), displayGain(1.0f), timeOffset(0.0f),
@@ -281,8 +292,6 @@ void LfpDisplayCanvas::update()
}
}
-
-
int LfpDisplayCanvas::getChannelHeight()
{
//return spreads[spreadSelection->getSelectedId()-1].getIntValue();
@@ -290,13 +299,11 @@ int LfpDisplayCanvas::getChannelHeight()
}
-
void LfpDisplayCanvas::setParameter(int param, float val)
{
// not used for anything, since LfpDisplayCanvas is not a processor
}
-
void LfpDisplayCanvas::refreshState()
{
// called when the component's tab becomes visible again
@@ -367,7 +374,6 @@ void LfpDisplayCanvas::updateScreenBuffer()
//if (channel == 15 || channel == 16)
// std::cout << channel << " " << sbi << " " << dbi << " " << nSamples << std::endl;
-
float ratio = sampleRate * timebase / float(getWidth() - leftmargin - scrollBarThickness); // samples / pixel
// this number is crucial: converting from samples to values (in px) for the screen buffer
int valuesNeeded = (int) float(nSamples) / ratio; // N pixels needed for this update
@@ -419,7 +425,6 @@ void LfpDisplayCanvas::updateScreenBuffer()
1, // numSamples
invAlpha*gain); // gain
-
screenBuffer->addFrom(channel, // destChannel
sbi, // destStartSample
displayBuffer->getReadPointer(channel, nextPos), // source
@@ -636,7 +641,6 @@ void LfpDisplayCanvas::redraw()
refresh();
}
-
void LfpDisplayCanvas::paint(Graphics& g)
{
@@ -670,7 +674,6 @@ void LfpDisplayCanvas::paint(Graphics& g)
g.drawLine(0,getHeight()-60,getWidth(),getHeight()-60,3.0f);
-
}
void LfpDisplayCanvas::refresh()
@@ -712,7 +715,6 @@ void LfpDisplayCanvas::saveVisualizerParameters(XmlElement* xml)
ed->saveVisualizerParameters(xml);
}
-
void LfpDisplayCanvas::loadVisualizerParameters(XmlElement* xml)
{
options->loadParameters(xml);
@@ -720,3763 +722,3 @@ void LfpDisplayCanvas::loadVisualizerParameters(XmlElement* xml)
ed->loadVisualizerParameters(xml);
}
-
-
-#pragma mark - ShowHideOptionsButton -
-// =============================================================
-
-
-ShowHideOptionsButton::ShowHideOptionsButton(LfpDisplayOptions* options) : Button("Button")
-{
- setClickingTogglesState(true);
-}
-ShowHideOptionsButton::~ShowHideOptionsButton()
-{
-
-}
-
-void ShowHideOptionsButton::paintButton(Graphics& g, bool, bool)
-{
- g.setColour(Colours::white);
-
- Path p;
-
- float h = getHeight();
- float w = getWidth();
-
- if (getToggleState())
- {
- p.addTriangle(0.5f*w, 0.2f*h,
- 0.2f*w, 0.8f*h,
- 0.8f*w, 0.8f*h);
- }
- else
- {
- p.addTriangle(0.8f*w, 0.8f*h,
- 0.2f*w, 0.5f*h,
- 0.8f*w, 0.2f*h);
- }
-
- PathStrokeType pst = PathStrokeType(1.0f, PathStrokeType::curved, PathStrokeType::rounded);
-
- g.strokePath(p, pst);
-}
-
-
-
-#pragma mark - LfpDisplayOptions -
-// -------------------------------------------------------------
-
-LfpDisplayOptions::LfpDisplayOptions(LfpDisplayCanvas* canvas_, LfpTimescale* timescale_,
- LfpDisplay* lfpDisplay_, LfpDisplayNode* processor_)
- : canvas(canvas_),
- lfpDisplay(lfpDisplay_),
- timescale(timescale_),
- processor(processor_),
- selectedChannelType(DataChannel::HEADSTAGE_CHANNEL),
- labelFont("Default", 13.0f, Font::plain),
- labelColour(100, 100, 100)
-{
- // draw the colour scheme options
- // TODO: (kelly) this might be better as a modal window
- colourSchemeOptionLabel = new Label("colorSchemeOptionLabel", "Color Scheme");
- colourSchemeOptionLabel->setFont(labelFont);
- colourSchemeOptionLabel->setColour(Label::textColourId, labelColour);
- addAndMakeVisible(colourSchemeOptionLabel);
-
- StringArray colourSchemeNames = lfpDisplay->getColourSchemeNameArray();
- colourSchemeOptionSelection = new ComboBox("colorSchemeOptionSelection");
- colourSchemeOptionSelection->addItemList(colourSchemeNames, 1);
- colourSchemeOptionSelection->setEditableText(false);
- colourSchemeOptionSelection->addListener(this);
- colourSchemeOptionSelection->setSelectedId(1, dontSendNotification);
- addAndMakeVisible(colourSchemeOptionSelection);
-
- if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
- addAndMakeVisible(lfpDisplay->getColourSchemePtr());
-
- //Ranges for neural data
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("25");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("50");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("100");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("250");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("400");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("500");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("750");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("1000");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("2000");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("5000");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("10000");
- voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("15000");
- selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL] = 4;
- rangeGain[DataChannel::HEADSTAGE_CHANNEL] = 1; //uV
- rangeSteps[DataChannel::HEADSTAGE_CHANNEL] = 10;
- rangeUnits.add(CharPointer_UTF8("\xC2\xB5V"));
- typeNames.add("DATA");
-
- UtilityButton* tbut;
- tbut = new UtilityButton("DATA",Font("Small Text", 9, Font::plain));
- tbut->setEnabledState(true);
- tbut->setCorners(false,false,false,false);
- tbut->addListener(this);
- tbut->setClickingTogglesState(true);
- tbut->setRadioGroupId(100,dontSendNotification);
- tbut->setToggleState(true,dontSendNotification);
- addAndMakeVisible(tbut);
- typeButtons.add(tbut);
-
- //Ranges for AUX/accelerometer data
- voltageRanges[DataChannel::AUX_CHANNEL].add("25");
- voltageRanges[DataChannel::AUX_CHANNEL].add("50");
- voltageRanges[DataChannel::AUX_CHANNEL].add("100");
- voltageRanges[DataChannel::AUX_CHANNEL].add("250");
- voltageRanges[DataChannel::AUX_CHANNEL].add("400");
- voltageRanges[DataChannel::AUX_CHANNEL].add("500");
- voltageRanges[DataChannel::AUX_CHANNEL].add("750");
- voltageRanges[DataChannel::AUX_CHANNEL].add("1000");
- voltageRanges[DataChannel::AUX_CHANNEL].add("2000");
- //voltageRanges[DataChannel::AUX_CHANNEL].add("5000");
- selectedVoltageRange[DataChannel::AUX_CHANNEL] = 9;
- rangeGain[DataChannel::AUX_CHANNEL] = 0.001f; //mV
- rangeSteps[DataChannel::AUX_CHANNEL] = 10;
- rangeUnits.add("mV");
- typeNames.add("AUX");
-
- tbut = new UtilityButton("AUX",Font("Small Text", 9, Font::plain));
- tbut->setEnabledState(true);
- tbut->setCorners(false,false,false,false);
- tbut->addListener(this);
- tbut->setClickingTogglesState(true);
- tbut->setRadioGroupId(100,dontSendNotification);
- tbut->setToggleState(false,dontSendNotification);
- addAndMakeVisible(tbut);
- typeButtons.add(tbut);
-
- //Ranges for ADC data
- voltageRanges[DataChannel::ADC_CHANNEL].add("0.01");
- voltageRanges[DataChannel::ADC_CHANNEL].add("0.05");
- voltageRanges[DataChannel::ADC_CHANNEL].add("0.1");
- voltageRanges[DataChannel::ADC_CHANNEL].add("0.5");
- voltageRanges[DataChannel::ADC_CHANNEL].add("1.0");
- voltageRanges[DataChannel::ADC_CHANNEL].add("2.0");
- voltageRanges[DataChannel::ADC_CHANNEL].add("5.0");
- voltageRanges[DataChannel::ADC_CHANNEL].add("10.0");
- selectedVoltageRange[DataChannel::ADC_CHANNEL] = 8;
- rangeGain[DataChannel::ADC_CHANNEL] = 1; //V
- rangeSteps[DataChannel::ADC_CHANNEL] = 0.1; //in V
- rangeUnits.add("V");
- typeNames.add("ADC");
-
- tbut = new UtilityButton("ADC",Font("Small Text", 9, Font::plain));
- tbut->setEnabledState(true);
- tbut->setCorners(false,false,false,false);
- tbut->addListener(this);
- tbut->setClickingTogglesState(true);
- tbut->setRadioGroupId(100,dontSendNotification);
- tbut->setToggleState(false,dontSendNotification);
- addAndMakeVisible(tbut);
- typeButtons.add(tbut);
-
- selectedVoltageRangeValues[DataChannel::HEADSTAGE_CHANNEL] = voltageRanges[DataChannel::HEADSTAGE_CHANNEL][selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL] - 1];
- selectedVoltageRangeValues[DataChannel::AUX_CHANNEL] = voltageRanges[DataChannel::AUX_CHANNEL][selectedVoltageRange[DataChannel::AUX_CHANNEL] - 1];
- selectedVoltageRangeValues[DataChannel::ADC_CHANNEL] = voltageRanges[DataChannel::ADC_CHANNEL][selectedVoltageRange[DataChannel::ADC_CHANNEL] - 1];
-
-
-
- // init channel display skipping options
- channelDisplaySkipOptions.add("All");
- channelDisplaySkipOptions.add("2");
- channelDisplaySkipOptions.add("4");
- channelDisplaySkipOptions.add("8");
- channelDisplaySkipOptions.add("16");
- channelDisplaySkipOptions.add("32");
- channelDisplaySkipOptions.add("64");
- selectedChannelDisplaySkip = 1;
- selectedChannelDisplaySkipValue = channelDisplaySkipOptions[selectedChannelDisplaySkip - 1];
-
- channelDisplaySkipSelection = new ComboBox("Channel Skip");
- channelDisplaySkipSelection->addItemList(channelDisplaySkipOptions, 1);
- channelDisplaySkipSelection->setSelectedId(selectedChannelDisplaySkip, sendNotification);
- channelDisplaySkipSelection->setEditableText(false);
- channelDisplaySkipSelection->addListener(this);
- addAndMakeVisible(channelDisplaySkipSelection);
-
- channelDisplaySkipLabel = new Label("Channel Display Skip", "Ch. Skip");
- channelDisplaySkipLabel->setFont(labelFont);
- channelDisplaySkipLabel->setColour(Label::textColourId, labelColour);
- addAndMakeVisible(channelDisplaySkipLabel);
-
-
-
- // init spike raster options
- spikeRasterSelectionOptions = {"Off", "-50", "-100", "-150", "-200", "-300", "-400", "-500"};
- selectedSpikeRasterThreshold = 1;
- selectedSpikeRasterThresholdValue = spikeRasterSelectionOptions[selectedSpikeRasterThreshold - 1];
-
- spikeRasterSelection = new ComboBox("spikeRasterSelection");
- spikeRasterSelection->addItemList(spikeRasterSelectionOptions, 1);
- spikeRasterSelection->setSelectedId(selectedSpikeRasterThreshold, dontSendNotification);
- spikeRasterSelection->setEditableText(true);
- spikeRasterSelection->addListener(this);
- addAndMakeVisible(spikeRasterSelection);
-
- spikeRasterLabel = new Label("spikeRasterLabel", "Spike Raster Thresh.");
- spikeRasterLabel->setFont(labelFont);
- spikeRasterLabel->setColour(Label::textColourId, labelColour);
- addAndMakeVisible(spikeRasterLabel);
-
-
-
- // init median offset plotting
- medianOffsetPlottingLabel = new Label("Median Offset Correction", "Median Offset Correction");
- medianOffsetPlottingLabel->setFont(labelFont);
- medianOffsetPlottingLabel->setColour(Label::textColourId, labelColour);
- addAndMakeVisible(medianOffsetPlottingLabel);
-
- medianOffsetPlottingButton = new UtilityButton("0", labelFont);
- medianOffsetPlottingButton->setRadius(5.0f);
- medianOffsetPlottingButton->setEnabledState(true);
- medianOffsetPlottingButton->setCorners(true, true, true, true);
- medianOffsetPlottingButton->addListener(this);
- medianOffsetPlottingButton->setClickingTogglesState(true);
- medianOffsetPlottingButton->setToggleState(false, sendNotification);
- addAndMakeVisible(medianOffsetPlottingButton);
-
- //init channel name toggle
- showChannelNumberLabel = new Label("showcChannelLabel", "Show channel number instead of name");
- showChannelNumberLabel->setFont(labelFont);
- showChannelNumberLabel->setColour(Label::textColourId, labelColour);
- addAndMakeVisible(showChannelNumberLabel);
-
- showChannelNumberButton = new UtilityButton("0", labelFont);
- showChannelNumberButton->setRadius(5.0f);
- showChannelNumberButton->setEnabledState(true);
- showChannelNumberButton->setCorners(true, true, true, true);
- showChannelNumberButton->addListener(this);
- showChannelNumberButton->setClickingTogglesState(true);
- showChannelNumberButton->setToggleState(false, sendNotification);
- addAndMakeVisible(showChannelNumberButton);
-
-
- // init show/hide options button
- showHideOptionsButton = new ShowHideOptionsButton(this);
- showHideOptionsButton->addListener(this);
- addAndMakeVisible(showHideOptionsButton);
-
- // init timebases options
- timebases.add("0.25");
- timebases.add("0.5");
- timebases.add("1.0");
- timebases.add("2.0");
- timebases.add("3.0");
- timebases.add("4.0");
- timebases.add("5.0");
- timebases.add("10.0");
- timebases.add("20.0");
- selectedTimebase = 4;
- selectedTimebaseValue = timebases[selectedTimebase-1];
-
- spreads.add("10");
- spreads.add("20");
- spreads.add("30");
- spreads.add("40");
- spreads.add("50");
- spreads.add("60");
- spreads.add("70");
- spreads.add("80");
- spreads.add("90");
- spreads.add("100");
- selectedSpread = 4;
- selectedSpreadValue = spreads[selectedSpread-1];
-
-
- overlaps.add("0.5");
- overlaps.add("0.75");
- overlaps.add("1");
- overlaps.add("2");
- overlaps.add("3");
- overlaps.add("4");
- overlaps.add("5");
- selectedOverlap = 4;
- selectedOverlapValue = overlaps[selectedOverlap-1];
-
- saturationThresholds.add("0.5");
- saturationThresholds.add("100");
- saturationThresholds.add("1000");
- saturationThresholds.add("5000");
- saturationThresholds.add("6389");
-
- selectedSaturation = 5;
- selectedSaturationValue = saturationThresholds[selectedSaturation-1];
-
-
- colorGroupings.add("1");
- colorGroupings.add("2");
- colorGroupings.add("4");
- colorGroupings.add("8");
- colorGroupings.add("16");
-
-
- rangeSelection = new ComboBox("Voltage range");
- rangeSelection->addItemList(voltageRanges[DataChannel::HEADSTAGE_CHANNEL], 1);
- rangeSelection->setSelectedId(selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL], sendNotification);
- rangeSelection->setEditableText(true);
- rangeSelection->addListener(this);
- addAndMakeVisible(rangeSelection);
-
-
- timebaseSelection = new ComboBox("Timebase");
- timebaseSelection->addItemList(timebases, 1);
- timebaseSelection->setSelectedId(selectedTimebase, sendNotification);
- timebaseSelection->setEditableText(true);
- timebaseSelection->addListener(this);
- addAndMakeVisible(timebaseSelection);
-
-
- spreadSelection = new ComboBox("Spread");
- spreadSelection->addItemList(spreads, 1);
- spreadSelection->setSelectedId(selectedSpread,sendNotification);
- spreadSelection->addListener(this);
- spreadSelection->setEditableText(true);
- addAndMakeVisible(spreadSelection);
-
- overlapSelection = new ComboBox("Overlap");
- overlapSelection->addItemList(overlaps, 1);
- overlapSelection->setSelectedId(selectedOverlap,sendNotification);
- overlapSelection->addListener(this);
- overlapSelection->setEditableText(true);
- addAndMakeVisible(overlapSelection);
-
- saturationWarningSelection = new ComboBox("Sat.Warn");
- saturationWarningSelection->addItemList(saturationThresholds, 1);
- saturationWarningSelection->setSelectedId(selectedSaturation,sendNotification);
- saturationWarningSelection->addListener(this);
- saturationWarningSelection->setEditableText(true);
- addAndMakeVisible(saturationWarningSelection);
-
-
- colorGroupingSelection = new ComboBox("Color Grouping");
- colorGroupingSelection->addItemList(colorGroupings, 1);
- colorGroupingSelection->setSelectedId(1,sendNotification);
- colorGroupingSelection->addListener(this);
- addAndMakeVisible(colorGroupingSelection);
-
- invertInputButton = new UtilityButton("Invert", Font("Small Text", 13, Font::plain));
- invertInputButton->setRadius(5.0f);
- invertInputButton->setEnabledState(true);
- invertInputButton->setCorners(true, true, true, true);
- invertInputButton->addListener(this);
- invertInputButton->setClickingTogglesState(true);
- invertInputButton->setToggleState(false, sendNotification);
- addAndMakeVisible(invertInputButton);
-
- // toggle button to reverse the order of channels
- reverseChannelsDisplayButton = new UtilityButton("0", labelFont);
- reverseChannelsDisplayButton->setRadius(5.0f);
- reverseChannelsDisplayButton->setEnabledState(true);
- reverseChannelsDisplayButton->setCorners(true, true, true, true);
- reverseChannelsDisplayButton->addListener(this);
- reverseChannelsDisplayButton->setClickingTogglesState(true);
- reverseChannelsDisplayButton->setToggleState(lfpDisplay->getChannelsReversed(), sendNotification);
- addAndMakeVisible(reverseChannelsDisplayButton);
-
- reverseChannelsDisplayLabel = new Label("Rev. Channels", "Rev. Channels");
- reverseChannelsDisplayLabel->setFont(labelFont);
- reverseChannelsDisplayLabel->setColour(Label::textColourId, labelColour);
- addAndMakeVisible(reverseChannelsDisplayLabel);
-
-
- //button for controlling drawing algorithm - old line-style or new per-pixel style
- drawMethodButton = new UtilityButton("DrawMethod", Font("Small Text", 13, Font::plain));
- drawMethodButton->setRadius(5.0f);
- drawMethodButton->setEnabledState(true);
- drawMethodButton->setCorners(true, true, true, true);
- drawMethodButton->addListener(this);
- drawMethodButton->setClickingTogglesState(true);
- drawMethodButton->setToggleState(false, sendNotification);
- addAndMakeVisible(drawMethodButton);
-
- // two sliders for the two histogram components of the supersampled plotting mode
- // todo: rename these
- brightnessSliderA = new Slider();
- brightnessSliderA->setRange (0, 1);
- brightnessSliderA->setTextBoxStyle(Slider::NoTextBox, false, 50,30);
- brightnessSliderA->addListener(this);
- addAndMakeVisible (brightnessSliderA);
-
- brightnessSliderB = new Slider;
- brightnessSliderB->setRange (0, 1);
- brightnessSliderB->setTextBoxStyle(Slider::NoTextBox, false, 50,30);
- brightnessSliderB->addListener(this);
- addAndMakeVisible (brightnessSliderB);
-
- sliderALabel = new Label("Brightness","Brightness");
- sliderALabel->setFont(Font("Small Text", 13, Font::plain));
- sliderALabel->setColour(Label::textColourId,Colour(150,150,150));
- addAndMakeVisible(sliderALabel);
-
- sliderBLabel = new Label("Min. brightness","Min. brightness");
- sliderBLabel->setFont(Font("Small Text", 13, Font::plain));
- sliderBLabel->setColour(Label::textColourId,Colour(150,150,150));
- addAndMakeVisible(sliderBLabel);
-
-
- //ScopedPointer drawClipWarningButton; // optinally draw (subtle) warning if data is clipped in display
- drawClipWarningButton = new UtilityButton("0", Font("Small Text", 13, Font::plain));
- drawClipWarningButton->setRadius(5.0f);
- drawClipWarningButton->setEnabledState(true);
- drawClipWarningButton->setCorners(true, true, true, true);
- drawClipWarningButton->addListener(this);
- drawClipWarningButton->setClickingTogglesState(true);
- drawClipWarningButton->setToggleState(false, sendNotification);
- addAndMakeVisible(drawClipWarningButton);
-
-
- //ScopedPointer drawSaturateWarningButton; // optionally raise hell if the actual data is saturating
- drawSaturateWarningButton = new UtilityButton("0", Font("Small Text", 13, Font::plain));
- drawSaturateWarningButton->setRadius(5.0f);
- drawSaturateWarningButton->setEnabledState(true);
- drawSaturateWarningButton->setCorners(true, true, true, true);
- drawSaturateWarningButton->addListener(this);
- drawSaturateWarningButton->setClickingTogglesState(true);
- drawSaturateWarningButton->setToggleState(false, sendNotification);
- addAndMakeVisible(drawSaturateWarningButton);
-
-
- //button for pausing the display - works by skipping buffer updates. This way scrolling etc still works
- pauseButton = new UtilityButton("Pause", Font("Small Text", 13, Font::plain));
- pauseButton->setRadius(5.0f);
- pauseButton->setEnabledState(true);
- pauseButton->setCorners(true, true, true, true);
- pauseButton->addListener(this);
- pauseButton->setClickingTogglesState(true);
- pauseButton->setToggleState(false, sendNotification);
- addAndMakeVisible(pauseButton);
-
- // add event display-specific controls (currently just an enable/disable button)
- for (int i = 0; i < 8; i++)
- {
-
- EventDisplayInterface* eventOptions = new EventDisplayInterface(lfpDisplay, canvas, i);
- eventDisplayInterfaces.add(eventOptions);
- addAndMakeVisible(eventOptions);
- eventOptions->setBounds(700+(floor(i/2)*20), getHeight()-20-(i%2)*20, 40, 20);
-
- lfpDisplay->setEventDisplayState(i,true);
-
- }
-
- lfpDisplay->setRange(voltageRanges[DataChannel::HEADSTAGE_CHANNEL][selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL] - 1].getFloatValue()*rangeGain[DataChannel::HEADSTAGE_CHANNEL]
- , DataChannel::HEADSTAGE_CHANNEL);
- lfpDisplay->setRange(voltageRanges[DataChannel::ADC_CHANNEL][selectedVoltageRange[DataChannel::ADC_CHANNEL] - 1].getFloatValue()*rangeGain[DataChannel::ADC_CHANNEL]
- , DataChannel::ADC_CHANNEL);
- lfpDisplay->setRange(voltageRanges[DataChannel::AUX_CHANNEL][selectedVoltageRange[DataChannel::AUX_CHANNEL] - 1].getFloatValue()*rangeGain[DataChannel::AUX_CHANNEL]
- , DataChannel::AUX_CHANNEL);
-
-}
-
-LfpDisplayOptions::~LfpDisplayOptions()
-{
-
-}
-
-void LfpDisplayOptions::resized()
-{
- rangeSelection->setBounds(5,getHeight()-30,80,25);
- timebaseSelection->setBounds(175,getHeight()-30,60,25);
-
- spreadSelection->setBounds(5,getHeight()-90,60,25);
-
- overlapSelection->setBounds(100,getHeight()-90,60,25);
-
- drawClipWarningButton->setBounds(175,getHeight()-89,20,20);
- drawSaturateWarningButton->setBounds(325, getHeight()-89, 20, 20);
-
- colorGroupingSelection->setBounds(400,getHeight()-90,60,25);
-
- invertInputButton->setBounds(35,getHeight()-190,100,22);
- drawMethodButton->setBounds(35,getHeight()-160,100,22);
-
- pauseButton->setBounds(450,getHeight()-50,50,44);
-
-
- // Reverse Channels Display
- reverseChannelsDisplayButton->setBounds(pauseButton->getRight() + 5,
- getHeight() - 50,
- 20,
- 20);
- reverseChannelsDisplayLabel->setBounds(reverseChannelsDisplayButton->getRight(),
- reverseChannelsDisplayButton->getY(),
- 120,
- 22);
-
- // Channel Display Skip Selector
- channelDisplaySkipSelection->setBounds(reverseChannelsDisplayButton->getX(),
- reverseChannelsDisplayButton->getBottom(),
- 60,
- 25);
- channelDisplaySkipLabel->setBounds(channelDisplaySkipSelection->getRight(),
- channelDisplaySkipSelection->getY() + 2,
- 100,
- 22);
-
- // Median Offset Plotting Button
- medianOffsetPlottingButton->setBounds(reverseChannelsDisplayLabel->getRight() + 5,
- reverseChannelsDisplayButton->getY(),
- 20,
- 20);
- medianOffsetPlottingLabel->setBounds(medianOffsetPlottingButton->getRight(),
- medianOffsetPlottingButton->getY(),
- 150,
- 22);
-
- //Channel name toggle
- showChannelNumberButton->setBounds(medianOffsetPlottingLabel->getRight() + 5,
- medianOffsetPlottingLabel->getY(),
- 20,
- 20);
- showChannelNumberLabel->setBounds(showChannelNumberButton->getRight(),
- showChannelNumberButton->getY(),
- 200,
- 22);
-
- // Spike raster plotting button
- spikeRasterSelection->setBounds(medianOffsetPlottingButton->getX(),
- medianOffsetPlottingButton->getBottom(),
- 60,
- 25);
- spikeRasterLabel->setBounds(spikeRasterSelection->getRight(),
- spikeRasterSelection->getY(),
- 120,
- 22);
-
-
- // Saturation Warning Selection
- saturationWarningSelection->setBounds(250, getHeight()-90, 60, 25);
-
-
- for (int i = 0; i < 8; i++)
- {
- eventDisplayInterfaces[i]->setBounds(300+(floor(i/2)*20), getHeight()-40+(i%2)*20, 40, 20); // arrange event channel buttons in two rows
- eventDisplayInterfaces[i]->repaint();
- }
-
- brightnessSliderA->setBounds(170,getHeight()-190,100,22);
- sliderALabel->setBounds(270, getHeight()-190, 180, 22);
- brightnessSliderA->setValue(0.9); //set default value
-
- brightnessSliderB->setBounds(170,getHeight()-160,100,22);
- sliderBLabel->setBounds(270, getHeight()-160, 180, 22);
- brightnessSliderB->setValue(0.1); //set default value
-
- showHideOptionsButton->setBounds (getWidth() - 28, getHeight() - 28, 20, 20);
-
- int bh = 25/typeButtons.size();
- for (int i = 0; i < typeButtons.size(); i++)
- {
- typeButtons[i]->setBounds(95,getHeight()-30+i*bh,50,bh);
- }
-
-
- colourSchemeOptionLabel->setBounds(medianOffsetPlottingButton->getX(),
- getHeight()-190,
- 100,
- 22);
- colourSchemeOptionSelection->setBounds(colourSchemeOptionLabel->getRight(),
- colourSchemeOptionLabel->getY(),
- 80,
- 25);
-
- // set the size of the active colour scheme's options, if it has configurable options
- if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
- {
- lfpDisplay->getColourSchemePtr()->setBounds(colourSchemeOptionLabel->getX(),
- colourSchemeOptionLabel->getBottom(),
- 200,
- 110);
- }
-}
-
-void LfpDisplayOptions::paint(Graphics& g)
-{
- int row1 = 55;
- int row2 = 110;
-
- g.fillAll(Colours::black);
- g.setFont(Font("Default", 16, Font::plain));
-
- g.setColour(Colour(100,100,100));
-
- g.drawText("Range("+ rangeUnits[selectedChannelType] +")",5,getHeight()-row1,300,20,Justification::left, false);
- g.drawText("Timebase(s)",160,getHeight()-row1,300,20,Justification::left, false);
- g.drawText("Size(px)",5,getHeight()-row2,300,20,Justification::left, false);
- g.drawText("Clip",100,getHeight()-row2,300,20,Justification::left, false);
- g.drawText("Warn",168,getHeight()-row2,300,20,Justification::left, false);
-
- g.drawText("Sat. Warning",225,getHeight()-row2,300,20,Justification::left, false);
-
- g.drawText("Color grouping",365,getHeight()-row2,300,20,Justification::left, false);
-
- g.drawText("Event disp.",300,getHeight()-row1,300,20,Justification::left, false);
-
- if(canvas->drawClipWarning)
- {
- g.setColour(Colours::white);
- g.fillRoundedRectangle(173,getHeight()-90-1,24,24,6.0f);
- }
-
- if(canvas->drawSaturationWarning)
- {
- g.setColour(Colours::red);
- g.fillRoundedRectangle(323,getHeight()-90-1,24,24,6.0f);
- }
-
-}
-
-int LfpDisplayOptions::getChannelHeight()
-{
- return (int)spreadSelection->getText().getIntValue();
-}
-
-
-bool LfpDisplayOptions::getDrawMethodState()
-{
-
- return drawMethodButton->getToggleState();
-}
-
-bool LfpDisplayOptions::getInputInvertedState()
-{
- return invertInputButton->getToggleState();
-}
-
-bool LfpDisplayOptions::getChannelNameState()
-{
- return showChannelNumberButton->getToggleState();
-}
-
-bool LfpDisplayOptions::getDisplaySpikeRasterizerState()
-{
-// return spikeRasterButton->getToggleState();
- return false;
-}
-
-void LfpDisplayOptions::setDisplaySpikeRasterizerState(bool isEnabled)
-{
-// spikeRasterButton->setToggleState(isEnabled, dontSendNotification);
-
-// if (isEnabled) medianOffsetPlottingButton->setToggleState(true, sendNotification);
-}
-
-void LfpDisplayOptions::setRangeSelection(float range, bool canvasMustUpdate)
-{
- if (canvasMustUpdate)
- {
- rangeSelection->setText(String(range/rangeGain[selectedChannelType]), sendNotification);
- }
- else
- {
- rangeSelection->setText(String(range/rangeGain[selectedChannelType]),dontSendNotification);
-
- selectedVoltageRange[selectedChannelType] = rangeSelection->getSelectedId();
- selectedVoltageRangeValues[selectedChannelType] = rangeSelection->getText();
-
- canvas->repaint();
- canvas->refresh();
- }
-
-}
-
-void LfpDisplayOptions::setSpreadSelection(int spread, bool canvasMustUpdate, bool deferDisplayRefresh)
-{
-
- if (canvasMustUpdate)
- {
- spreadSelection->setText(String(spread),sendNotification);
- }
- else
- {
- spreadSelection->setText(String(spread),dontSendNotification);
- selectedSpread = spreadSelection->getSelectedId();
- selectedSpreadValue = spreadSelection->getText();
-
- if (!deferDisplayRefresh)
- {
- canvas->repaint();
- canvas->refresh();
- }
- }
-}
-
-void LfpDisplayOptions::togglePauseButton(bool sendUpdate)
-{
- pauseButton->setToggleState(!pauseButton->getToggleState(), sendUpdate ? sendNotification : dontSendNotification);
-}
-
-void LfpDisplayOptions::buttonClicked(Button* b)
-{
- if (b == invertInputButton)
- {
- lfpDisplay->setInputInverted(b->getToggleState());
- return;
- }
- if (b == reverseChannelsDisplayButton)
- {
- lfpDisplay->setChannelsReversed(b->getToggleState());
- return;
- }
- if (b == medianOffsetPlottingButton)
- {
- if (lfpDisplay->getSpikeRasterPlotting())
- {
- medianOffsetPlottingButton->setToggleState(true, dontSendNotification);
- }
- else
- {
- lfpDisplay->setMedianOffsetPlotting(b->getToggleState());
- }
- return;
- }
- if (b == drawMethodButton)
- {
- lfpDisplay->setDrawMethod(b->getToggleState()); // this should be done the same way as drawClipWarning - or the other way around.
-
- return;
- }
- if (b == drawClipWarningButton)
- {
- canvas->drawClipWarning = b->getToggleState();
- canvas->redraw();
- return;
- }
- if (b == drawSaturateWarningButton)
- {
- canvas->drawSaturationWarning = b->getToggleState();
- canvas->redraw();
- return;
- }
-
- if (b == pauseButton)
- {
- lfpDisplay->isPaused = b->getToggleState();
- return;
- }
-
- if (b == showHideOptionsButton)
- {
- canvas->toggleOptionsDrawer(b->getToggleState());
- }
-
- if (b == showChannelNumberButton)
- {
- int numChannels = lfpDisplay->channelInfo.size();
- for (int i = 0; i < numChannels; ++i)
- {
- lfpDisplay->channelInfo[i]->repaint();
- }
- return;
- }
-
- int idx = typeButtons.indexOf((UtilityButton*)b);
-
- if ((idx >= 0) && (b->getToggleState()))
- {
- for (int i = 0; i < lfpDisplay->getNumChannels(); i++)
- {
- if (lfpDisplay->channels[i]->getSelected())
- {
- lfpDisplay->channels[i]->deselect();
- lfpDisplay->channels[i]->repaint();
- }
- }
-
- setSelectedType((DataChannel::DataChannelTypes) idx, false);
- }
-
-}
-
-void LfpDisplayOptions::setTimebaseAndSelectionText(float timebase)
-{
- canvas->timebase = timebase;
-
- if (canvas->timebase) // if timebase != 0
- {
- if (canvas->timebase < timebases[0].getFloatValue())
- {
- timebaseSelection->setSelectedId(1, dontSendNotification);
- canvas->timebase = timebases[0].getFloatValue();
- }
- else if (canvas->timebase > timebases[timebases.size()-1].getFloatValue())
- {
- timebaseSelection->setSelectedId(timebases.size(), dontSendNotification);
- canvas->timebase = timebases[timebases.size()-1].getFloatValue();
- }
- else{
- timebaseSelection->setText(String(canvas->timebase, 1), dontSendNotification);
- }
- }
- else
- {
- if (selectedSpread == 0)
- {
- timebaseSelection->setText(selectedTimebaseValue, dontSendNotification);
- canvas->timebase = selectedTimebaseValue.getFloatValue();
- }
- else
- {
- timebaseSelection->setSelectedId(selectedTimebase,dontSendNotification);
- canvas->timebase = timebases[selectedTimebase-1].getFloatValue();
- }
-
- }
-}
-
-
-void LfpDisplayOptions::comboBoxChanged(ComboBox* cb)
-{
- if (canvas->getNumChannels() == 0) return;
-
- if (cb == channelDisplaySkipSelection)
- {
- const int skipAmt = pow(2, cb->getSelectedId() - 1);
- lfpDisplay->setChannelDisplaySkipAmount(skipAmt);
- }
- else if (cb == spikeRasterSelection)
- {
- // if custom value
- if (cb->getSelectedId() == 0)
- {
- auto val = fabsf(cb->getText().getFloatValue());
-
- if (val == 0) // if value is zero, just disable plotting and set text to "Off"
- {
- cb->setSelectedItemIndex(0, dontSendNotification);
- lfpDisplay->setSpikeRasterPlotting(false);
- return;
- }
-
- if (val > 500)
- {
- val = 500;
- }
-
- val *= -1;
-
- spikeRasterSelection->setText(String(val), dontSendNotification);
- lfpDisplay->setSpikeRasterThreshold(val);
- medianOffsetPlottingButton->setToggleState(true, dontSendNotification);
- lfpDisplay->setMedianOffsetPlotting(true);
- lfpDisplay->setSpikeRasterPlotting(true);
- }
- else if (cb->getSelectedItemIndex() == 0) // if "Off"
- {
- lfpDisplay->setSpikeRasterPlotting(false);
- return;
- }
- else
- {
- auto val = cb->getText().getFloatValue();
-
- lfpDisplay->setSpikeRasterThreshold(val);
- medianOffsetPlottingButton->setToggleState(true, dontSendNotification);
- lfpDisplay->setMedianOffsetPlotting(true);
- lfpDisplay->setSpikeRasterPlotting(true);
- }
- }
- else if (cb == colourSchemeOptionSelection)
- {
- // hide the old colour scheme config options if they are displayed
- if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
- removeChildComponent(lfpDisplay->getColourSchemePtr());
-
- // change the active colour scheme ptr
- lfpDisplay->setActiveColourSchemeIdx(cb->getSelectedId()-1);
-
- // show the new colour scheme's config options if has any
-
- if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
- {
- lfpDisplay->getColourSchemePtr()->setBounds(colourSchemeOptionLabel->getX(),
- colourSchemeOptionLabel->getBottom(),
- 200,
- 110);
- addAndMakeVisible(lfpDisplay->getColourSchemePtr());
- }
-
- // update the lfpDisplay's colors and redraw
- lfpDisplay->setColors();
- canvas->redraw();
- }
- else if (cb == timebaseSelection)
- {
- if (cb->getSelectedId())
- {
- canvas->timebase = timebases[cb->getSelectedId()-1].getFloatValue();
- }
- else
- {
- setTimebaseAndSelectionText(cb->getText().getFloatValue());
- }
- }
- else if (cb == rangeSelection)
- {
- if (cb->getSelectedId())
- {
- lfpDisplay->setRange(voltageRanges[selectedChannelType][cb->getSelectedId()-1].getFloatValue()*rangeGain[selectedChannelType]
- ,selectedChannelType);
- }
- else
- {
- float vRange = cb->getText().getFloatValue();
- if (vRange)
- {
- if (vRange < voltageRanges[selectedChannelType][0].getFloatValue())
- {
- cb->setSelectedId(1,dontSendNotification);
- vRange = voltageRanges[selectedChannelType][0].getFloatValue();
- }
- else if (vRange > voltageRanges[selectedChannelType][voltageRanges[selectedChannelType].size()-1].getFloatValue())
- {
- // cb->setSelectedId(voltageRanges[selectedChannelType].size(),dontSendNotification);
- // vRange = voltageRanges[selectedChannelType][voltageRanges[selectedChannelType].size()-1].getFloatValue();
- }
- else
- {
- if (rangeGain[selectedChannelType] > 1)
- cb->setText(String(vRange,1),dontSendNotification);
- else
- cb->setText(String(vRange),dontSendNotification);
- }
- lfpDisplay->setRange(vRange*rangeGain[selectedChannelType],selectedChannelType);
- }
- else
- {
- if (selectedVoltageRange[selectedChannelType])
- cb->setText(selectedVoltageRangeValues[selectedChannelType],dontSendNotification);
- else
- cb->setSelectedId(selectedVoltageRange[selectedChannelType],dontSendNotification);
- }
- }
- selectedVoltageRange[selectedChannelType] = cb->getSelectedId();
- selectedVoltageRangeValues[selectedChannelType] = cb->getText();
- //std::cout << "Setting range to " << voltageRanges[cb->getSelectedId()-1].getFloatValue() << std::endl;
- canvas->redraw();
- }
- else if (cb == spreadSelection)
- {
-
- if (cb->getSelectedId())
- {
- if (lfpDisplay->getSingleChannelState())
- {
- lfpDisplay->cacheNewChannelHeight(spreads[cb->getSelectedId()-1].getIntValue());
- }
- else
- {
- lfpDisplay->setChannelHeight(spreads[cb->getSelectedId()-1].getIntValue());
- resized();
- }
- }
- else
- {
- int spread = cb->getText().getIntValue();
- if (spread)
- {
- if (spread < spreads[0].getFloatValue())
- {
- cb->setSelectedId(1,dontSendNotification);
- spread = spreads[0].getFloatValue();
- }
- else if (spread > spreads[spreads.size()-1].getFloatValue())
- {
- cb->setSelectedId(spreads.size(),dontSendNotification);
- spread = spreads[spreads.size()-1].getFloatValue();
- }
- else
- {
- cb->setText(String(spread),dontSendNotification);
- }
-
- // if single channel focus is on, cache the value
- if (lfpDisplay->getSingleChannelState())
- {
- lfpDisplay->cacheNewChannelHeight(spread);
- }
- else
- {
- lfpDisplay->setChannelHeight(spread);
- canvas->resized();
- }
- }
- else
- {
- if (selectedSpread == 0)
- cb->setText(selectedSpreadValue,dontSendNotification);
- else
- cb->setSelectedId(selectedSpread,dontSendNotification);
- }
- }
- selectedSpread = cb->getSelectedId();
- selectedSpreadValue = cb->getText();
-
- if (!lfpDisplay->getSingleChannelState()) canvas->redraw();
- //std::cout << "Setting spread to " << spreads[cb->getSelectedId()-1].getFloatValue() << std::endl;
- }
- else if (cb == saturationWarningSelection)
- {
- if (cb->getSelectedId())
- {
- selectedSaturationValueFloat = (saturationThresholds[cb->getSelectedId()-1].getFloatValue());
- }
- else
- {
- selectedSaturationValueFloat = cb->getText().getFloatValue();
- if (selectedSaturationValueFloat)
- {
- std::cout << "Setting saturation warning to to " << selectedSaturationValueFloat << std::endl;
- if (selectedSaturationValueFloat < 0)
- {
- cb->setSelectedId(1,dontSendNotification);
- selectedSaturationValueFloat = saturationThresholds[0].getFloatValue();
- }
- else
- {
- // cb->setText(String(selectedSaturationValueFloat),dontSendNotification);
- }
- }
- else
- {
- // cb->setSelectedId(1,dontSendNotification);
- //selectedSaturationValueFloat = saturationThresholds[0].getFloatValue();
-
- }
- }
- canvas->redraw();
-
- std::cout << "Setting saturation warning to to " << selectedSaturationValueFloat << std::endl;
- }
- else if (cb == overlapSelection)
- {
- if (cb->getSelectedId())
- {
- canvas->channelOverlapFactor = (overlaps[cb->getSelectedId()-1].getFloatValue());
- canvas->resized();
- }
- else
- {
- float overlap = cb->getText().getFloatValue();
- if (overlap)
- {
- if (overlap < overlaps[0].getFloatValue())
- {
- cb->setSelectedId(1,dontSendNotification);
- overlap = overlaps[0].getFloatValue();
- }
- else if (overlap > overlaps[overlaps.size()-1].getFloatValue())
- {
- cb->setSelectedId(overlaps.size(),dontSendNotification);
- overlap = overlaps[overlaps.size()-1].getFloatValue();
- }
- else
- {
- cb->setText(String(overlap),dontSendNotification);
- }
- canvas->channelOverlapFactor= overlap;
- canvas->resized();
- }
- else
- {
- if (selectedSpread == 0)
- cb->setText(selectedSpreadValue,dontSendNotification);
- else
- cb->setSelectedId(selectedSpread,dontSendNotification);
- }
- }
- selectedSpread = cb->getSelectedId();
- selectedSpreadValue = cb->getText();
- lfpDisplay->setChannelHeight( lfpDisplay->getChannelHeight());
- canvas->redraw();
- //std::cout << "Setting spread to " << spreads[cb->getSelectedId()-1].getFloatValue() << std::endl;
- }
-
- else if (cb == colorGroupingSelection)
- {
- // set color grouping here
- lfpDisplay->setColorGrouping(colorGroupings[cb->getSelectedId()-1].getIntValue());// so that channel colors get re-assigned
- canvas->redraw();
- }
-
- timescale->setTimebase(canvas->timebase);
-}
-
-
-void LfpDisplayOptions::sliderValueChanged(Slider* sl)
-{
- if (sl == brightnessSliderA)
- canvas->histogramParameterA = sl->getValue();
-
- if (sl == brightnessSliderB)
- canvas->histogramParameterB = sl->getValue();
-
-
- canvas->fullredraw=true;
- //repaint();
- canvas->refresh();
-
-}
-
-void LfpDisplayOptions::sliderEvent(Slider* sl) {}
-
-DataChannel::DataChannelTypes LfpDisplayOptions::getChannelType(int n)
-{
- if (n < processor->getNumInputs())
- return processor->getDataChannel(n)->getChannelType();
- else
- return DataChannel::HEADSTAGE_CHANNEL;
-}
-
-DataChannel::DataChannelTypes LfpDisplayOptions::getSelectedType()
-{
- return selectedChannelType;
-}
-
-void LfpDisplayOptions::setSelectedType(DataChannel::DataChannelTypes type, bool toggleButton)
-{
- if (selectedChannelType == type)
- return; //Nothing to do here
- selectedChannelType = type;
- rangeSelection->clear(dontSendNotification);
- rangeSelection->addItemList(voltageRanges[type],1);
-
- int id = selectedVoltageRange[type];
- if (id)
- rangeSelection->setSelectedId(id,sendNotification);
- else
- rangeSelection->setText(selectedVoltageRangeValues[selectedChannelType],dontSendNotification);
-
- repaint(5,getHeight()-55,300,100);
-
- if (toggleButton)
- typeButtons[type]->setToggleState(true,dontSendNotification);
-}
-
-String LfpDisplayOptions::getTypeName(DataChannel::DataChannelTypes type)
-{
- return typeNames[type];
-}
-
-int LfpDisplayOptions::getRangeStep(DataChannel::DataChannelTypes type)
-{
- return rangeSteps[type];
-}
-
-
-
-void LfpDisplayOptions::saveParameters(XmlElement* xml)
-{
- // TODO: (kelly) add savers for:
- // - channel reverse
- // - channel zoom slider
- // - channel display skip
- std::cout << "Saving lfp display params" << std::endl;
-
- XmlElement* xmlNode = xml->createNewChildElement("LFPDISPLAY");
-
- lfpDisplay->reactivateChannels();
-
- xmlNode->setAttribute("Range",selectedVoltageRangeValues[0]+","+selectedVoltageRangeValues[1]+
- ","+selectedVoltageRangeValues[2]);
- xmlNode->setAttribute("Timebase",timebaseSelection->getText());
- xmlNode->setAttribute("Spread",spreadSelection->getText());
- xmlNode->setAttribute("colorGrouping",colorGroupingSelection->getSelectedId());
- xmlNode->setAttribute("isInverted",invertInputButton->getToggleState());
- xmlNode->setAttribute("drawMethod",drawMethodButton->getToggleState());
-
- int eventButtonState = 0;
-
- for (int i = 0; i < 8; i++)
- {
- if (lfpDisplay->eventDisplayEnabled[i])
- {
- eventButtonState += (1 << i);
- }
- }
-
- lfpDisplay->reactivateChannels();
-
- xmlNode->setAttribute("EventButtonState", eventButtonState);
-
- String channelDisplayState = "";
-
- for (int i = 0; i < canvas->nChans; i++)
- {
- if (lfpDisplay->getEnabledState(i))
- {
- channelDisplayState += "1";
- }
- else
- {
- channelDisplayState += "0";
- }
- //std::cout << channelDisplayState;
- }
-
- //std::cout << std::endl;
-
-
- xmlNode->setAttribute("ChannelDisplayState", channelDisplayState);
-
- xmlNode->setAttribute("ScrollX",canvas->viewport->getViewPositionX());
- xmlNode->setAttribute("ScrollY",canvas->viewport->getViewPositionY());
-}
-
-
-void LfpDisplayOptions::loadParameters(XmlElement* xml)
-{
- // TODO: (kelly) add loaders for:
- // - channel reverse
- // - channel zoom slider
- // - channel display skip
- forEachXmlChildElement(*xml, xmlNode)
- {
- if (xmlNode->hasTagName("LFPDISPLAY"))
- {
- StringArray ranges;
- ranges.addTokens(xmlNode->getStringAttribute("Range"),",",String::empty);
- selectedVoltageRangeValues[0] = ranges[0];
- selectedVoltageRangeValues[1] = ranges[1];
- selectedVoltageRangeValues[2] = ranges[2];
- selectedVoltageRange[0] = voltageRanges[0].indexOf(ranges[0])+1;
- selectedVoltageRange[1] = voltageRanges[1].indexOf(ranges[1])+1;
- selectedVoltageRange[2] = voltageRanges[2].indexOf(ranges[2])+1;
- rangeSelection->setText(ranges[0]);
-
- timebaseSelection->setText(xmlNode->getStringAttribute("Timebase"));
- spreadSelection->setText(xmlNode->getStringAttribute("Spread"));
- if (xmlNode->hasAttribute("colorGrouping"))
- {
- colorGroupingSelection->setSelectedId(xmlNode->getIntAttribute("colorGrouping"));
- }
- else
- {
- colorGroupingSelection->setSelectedId(1);
- }
-
- invertInputButton->setToggleState(xmlNode->getBoolAttribute("isInverted", true), sendNotification);
-
- drawMethodButton->setToggleState(xmlNode->getBoolAttribute("drawMethod", true), sendNotification);
-
- canvas->viewport->setViewPosition(xmlNode->getIntAttribute("ScrollX"),
- xmlNode->getIntAttribute("ScrollY"));
-
- int eventButtonState = xmlNode->getIntAttribute("EventButtonState");
-
- for (int i = 0; i < 8; i++)
- {
- lfpDisplay->eventDisplayEnabled[i] = (eventButtonState >> i) & 1;
-
- eventDisplayInterfaces[i]->checkEnabledState();
- }
-
- String channelDisplayState = xmlNode->getStringAttribute("ChannelDisplayState");
-
- for (int i = 0; i < channelDisplayState.length(); i++)
- {
-
- if (channelDisplayState.substring(i,i+1).equalsIgnoreCase("1"))
- {
- //std::cout << "LfpDisplayCanvas enabling channel " << i << std::endl;
- //lfpDisplay->enableChannel(true, i);
- canvas->isChannelEnabled.set(i,true); //lfpDisplay->enableChannel(true, i);
- }
- else
- {
- //lfpDisplay->enableChannel(false, i);
- canvas->isChannelEnabled.set(i,false);
- }
-
-
- }
- }
- }
-
-}
-
-
-#pragma mark - LfpTimescale -
-// -------------------------------------------------------------
-
-LfpTimescale::LfpTimescale(LfpDisplayCanvas* c, LfpDisplay* lfpDisplay)
- : canvas(c)
- , lfpDisplay(lfpDisplay)
-{
-
- font = Font("Default", 16, Font::plain);
-}
-
-LfpTimescale::~LfpTimescale()
-{
-
-}
-
-void LfpTimescale::paint(Graphics& g)
-{
-
- g.setFont(font);
-
- g.setColour(Colour(100,100,100));
-
- const String timeScaleUnitLabel = (timebase >= 2)?("s:"):("ms:");
- g.drawText(timeScaleUnitLabel,5,0,100,getHeight(),Justification::left, false);
-
- const int steps = labels.size() + 1;
- for (int i = 0; i < steps; i++)
- {
-
- // TODO: (kelly) added an extra spatial dimension to the timeline ticks, may be overkill
- if (i == 0)
- {
- g.drawLine(1,
- 0,
- 1,
- getHeight(),
- 3.0f);
- }
- if (i != 0 && i % 4 == 0)
- {
- g.drawLine(getWidth()/steps*i,
- 0,
- getWidth()/steps*i,
- getHeight(),
- 3.0f);
- }
- else if (i != 0 && i % 2 == 0)
- {
- g.drawLine(getWidth()/steps*i,
- getHeight(),
- getWidth()/steps*i,
- getHeight() / 2,
- 3.0f);
- }
- else
- {
- g.drawLine(getWidth()/steps*i,
- getHeight(),
- getWidth()/steps*i,
- 3 * getHeight()/4,
- 2.0f);
- }
-
-
- if (i != 0 && i % 2 == 0)
- g.drawText(labels[i-1],getWidth()/steps*i+3,0,100,getHeight(),Justification::left, false);
-
- }
-}
-
-void LfpTimescale::mouseUp(const MouseEvent &e)
-{
- if (e.mods.isLeftButtonDown())
- {
- lfpDisplay->trackZoomInfo.isScrollingX = false;
- }
-}
-
-void LfpTimescale::resized()
-{
- setTimebase(timebase);
-}
-
-void LfpTimescale::mouseDrag(const juce::MouseEvent &e)
-{
- if (e.mods.isLeftButtonDown()) // double check that we initiate only for left click and hold
- {
- if (e.mods.isCommandDown()) // CTRL + drag -> change channel spacing
- {
- // init state in our track zooming info struct
- if (!lfpDisplay->trackZoomInfo.isScrollingX)
- {
- lfpDisplay->trackZoomInfo.isScrollingX = true;
- lfpDisplay->trackZoomInfo.timescaleStartScale = timebase;
- }
-
- float timescale = lfpDisplay->trackZoomInfo.timescaleStartScale;
- float dTimescale=0;
- int dragDeltaX = (e.getScreenPosition().getX() - e.getMouseDownScreenX()); // invert so drag up -> scale up
-
-// std::cout << dragDeltaX << std::endl;
- if (dragDeltaX > 0)
- {
- dTimescale = 0.01 * dragDeltaX;
- }
- else
- {
- // TODO: (kelly) change this to scale appropriately for -dragDeltaX
- if (timescale > 0.25)
- dTimescale = 0.01 * dragDeltaX;
- }
-
- if (timescale >= 1) // accelerate scrolling for large ranges
- dTimescale *= 4;
-
- if (timescale >= 5)
- dTimescale *= 4;
-
- if (timescale >= 10)
- dTimescale *= 4;
-
- // round dTimescale to the nearest 0.005 sec
- dTimescale = ((dTimescale + (0.005/2)) / 0.005) * 0.005;
-
- float newTimescale = timescale+dTimescale;
-
- if (newTimescale < 0.25) newTimescale = 0.250;
- if (newTimescale > 20) newTimescale = 20;
-
- // don't bother updating if the new timebase is the same as the old (if clipped, for example)
- if (timescale != newTimescale)
- {
- lfpDisplay->options->setTimebaseAndSelectionText(newTimescale);
- setTimebase(canvas->timebase);
- }
- }
- }
-}
-
-void LfpTimescale::setTimebase(float t)
-{
- timebase = t;
-
- labels.clear();
-
- const int minWidth = 60;
- labelIncrement = 0.025f;
-
-
- while (getWidth() != 0 && // setTimebase can be called before LfpTimescale has width
- getWidth() / (timebase / labelIncrement) < minWidth) // so, if width is 0 then don't iterate for scale factor
- {
-// std::cout << getWidth() / (timebase / labelIncrement) << " is smaller than minimum width, calculating new step size" << std::endl;
- if (labelIncrement < 0.2)
- labelIncrement *= 2;
- else
- labelIncrement += 0.2f;
- }
-
- for (float i = labelIncrement; i < timebase; i += labelIncrement)
- {
- String labelString = String(i * ((timebase >= 2)?(1):(1000.0f)));
- labels.add(labelString.substring(0,6));
- }
-
- repaint();
-
-}
-
-
-
-#pragma mark - LfpDisplay -
-// ---------------------------------------------------------------
-
-LfpDisplay::LfpDisplay(LfpDisplayCanvas* c, Viewport* v)
- : singleChan(-1)
- , canvas(c)
- , viewport(v)
- , channelsReversed(false)
- , displaySkipAmt(0)
- , m_SpikeRasterPlottingFlag(false)
-{
- perPixelPlotter = new PerPixelBitmapPlotter(this);
- supersampledPlotter = new SupersampledBitmapPlotter(this);
-
-// colorScheme = new LfpDefaultColourScheme();
- colourSchemeList.add(new LfpDefaultColourScheme(this, canvas));
- colourSchemeList.add(new LfpMonochromaticColourScheme(this, canvas));
- colourSchemeList.add(new LfpGradientColourScheme(this, canvas));
-
- activeColourScheme = 0;
-
-
- plotter = perPixelPlotter;
- m_MedianOffsetPlottingFlag = false;
-
- totalHeight = 0;
- colorGrouping=1;
-
- range[0] = 1000;
- range[1] = 500;
- range[2] = 500000;
-
- addMouseListener(this, true);
-
- // hue cycle
- //for (int i = 0; i < 15; i++)
- //{
- // channelColours.add(Colour(float(sin((3.14/2)*(float(i)/15))),float(1.0),float(1),float(1.0)));
- //}
-
-// setBufferedToImage(true); // TODO: (kelly) test
-
- backgroundColour = Colour(0,18,43);
-
- //hand-built palette
- channelColours.add(Colour(224,185,36));
- channelColours.add(Colour(214,210,182));
- channelColours.add(Colour(243,119,33));
- channelColours.add(Colour(186,157,168));
- channelColours.add(Colour(237,37,36));
- channelColours.add(Colour(179,122,79));
- channelColours.add(Colour(217,46,171));
- channelColours.add(Colour(217, 139,196));
- channelColours.add(Colour(101,31,255));
- channelColours.add(Colour(141,111,181));
- channelColours.add(Colour(48,117,255));
- channelColours.add(Colour(184,198,224));
- channelColours.add(Colour(116,227,156));
- channelColours.add(Colour(150,158,155));
- channelColours.add(Colour(82,173,0));
- channelColours.add(Colour(125,99,32));
-
- isPaused=false;
-
-}
-
-LfpDisplay::~LfpDisplay()
-{
-// deleteAllChildren();
-}
-
-
-
-int LfpDisplay::getNumChannels()
-{
- return numChans;
-}
-
-
-
-int LfpDisplay::getColorGrouping()
-{
- return colorGrouping;
-}
-
-void LfpDisplay::setColorGrouping(int i)
-{
- colorGrouping=i;
- getColourSchemePtr()->setColourGrouping(i);
- setColors(); // so that channel colors get re-assigned
-
-}
-
-LfpChannelColourScheme * LfpDisplay::getColourSchemePtr()
-{
- return colourSchemeList[activeColourScheme];
-}
-
-void LfpDisplay::setNumChannels(int numChannels)
-{
- numChans = numChannels;
-
-
-// deleteAllChildren();
- removeAllChildren();
-
- channels.clear();
- channelInfo.clear();
- drawableChannels.clear();
-
- totalHeight = 0;
- cachedDisplayChannelHeight = canvas->getChannelHeight();
-
- if (numChans > 0)
- {
- for (int i = 0; i < numChans; i++)
- {
- //std::cout << "Adding new display for channel " << i << std::endl;
-
- LfpChannelDisplay* lfpChan = new LfpChannelDisplay(canvas, this, options, i);
-
- //lfpChan->setColour(channelColours[i % channelColours.size()]);
- lfpChan->setRange(range[options->getChannelType(i)]);
- lfpChan->setChannelHeight(canvas->getChannelHeight());
-
- addAndMakeVisible(lfpChan);
-
- channels.add(lfpChan);
-
- LfpChannelDisplayInfo* lfpInfo = new LfpChannelDisplayInfo(canvas, this, options, i);
-
- //lfpInfo->setColour(channelColours[i % channelColours.size()]);
- lfpInfo->setRange(range[options->getChannelType(i)]);
- lfpInfo->setChannelHeight(canvas->getChannelHeight());
- lfpInfo->setSubprocessorIdx(canvas->getChannelSubprocessorIdx(i));
-
- addAndMakeVisible(lfpInfo);
-
- channelInfo.add(lfpInfo);
-
- drawableChannels.add(LfpChannelTrack{
- lfpChan,
- lfpInfo
- });
-
- savedChannelState.add(true);
-
- totalHeight += lfpChan->getChannelHeight();
-
- }
-
- }
-
- setColors();
-
-
- std::cout << "TOTAL HEIGHT = " << totalHeight << std::endl;
-
-}
-
-void LfpDisplay::setColors()
-{
- for (int i = 0; i < drawableChannels.size(); i++)
- {
-
-// channels[i]->setColour(channelColours[(int(i/colorGrouping)+1) % channelColours.size()]);
-// channelInfo[i]->setColour(channelColours[(int(i/colorGrouping)+1) % channelColours.size()]);
- drawableChannels[i].channel->setColour(getColourSchemePtr()->getColourForIndex(i));
- drawableChannels[i].channelInfo->setColour(getColourSchemePtr()->getColourForIndex(i));
- }
-
-}
-
-void LfpDisplay::setActiveColourSchemeIdx(int index)
-{
- activeColourScheme = index;
-}
-
-int LfpDisplay::getActiveColourSchemeIdx()
-{
- return activeColourScheme;
-}
-
-int LfpDisplay::getNumColourSchemes()
-{
- return colourSchemeList.size();
-}
-
-StringArray LfpDisplay::getColourSchemeNameArray()
-{
- StringArray nameList;
- for (auto scheme : colourSchemeList)
- nameList.add(scheme->getName());
-
- return nameList;
-}
-
-int LfpDisplay::getTotalHeight()
-{
- return totalHeight;
-}
-
-void LfpDisplay::resized()
-{
- int totalHeight = 0;
-
- for (int i = 0; i < drawableChannels.size(); i++)
- {
-
- LfpChannelDisplay* disp = drawableChannels[i].channel;
-
- if (disp->getHidden()) continue;
-
- disp->setBounds(canvas->leftmargin,
- totalHeight-(disp->getChannelOverlap()*canvas->channelOverlapFactor)/2,
- getWidth(),
- disp->getChannelHeight()+(disp->getChannelOverlap()*canvas->channelOverlapFactor));
-
- disp-> resized();
-
- LfpChannelDisplayInfo* info = drawableChannels[i].channelInfo;
-
- info->setBounds(0,
- totalHeight-disp->getChannelHeight() + (disp->getChannelOverlap()*canvas->channelOverlapFactor)/4.0,
- canvas->leftmargin + 50,
- disp->getChannelHeight());
-
- totalHeight += disp->getChannelHeight();
-
- }
-
- canvas->fullredraw = true; //issue full redraw
- if (singleChan != -1)
- viewport->setViewPosition(Point(0,singleChan*getChannelHeight()));
-
-
-
- lfpChannelBitmap = Image(Image::ARGB, getWidth(), getHeight(), false);
-
- //inititalize black background
- Graphics gLfpChannelBitmap(lfpChannelBitmap);
- gLfpChannelBitmap.setColour(Colour(0,0,0)); //background color
- gLfpChannelBitmap.fillRect(0,0, getWidth(), getHeight());
-
-
- canvas->fullredraw = true;
-
- refresh();
- // std::cout << "Total height: " << totalHeight << std::endl;
-
-}
-
-void LfpDisplay::paint(Graphics& g)
-{
-
- g.drawImageAt(lfpChannelBitmap, canvas->leftmargin,0);
-
-}
-
-
-void LfpDisplay::refresh()
-{
- // Ensure the lfpChannelBitmap has been initialized
- if (lfpChannelBitmap.isNull())
- {
- resized();
- }
-
- // X-bounds of this update
- int fillfrom = canvas->lastScreenBufferIndex[0];
- int fillto = (canvas->screenBufferIndex[0]);
-
- if (fillfrom<0){fillfrom=0;};
- if (fillto>lfpChannelBitmap.getWidth()){fillto=lfpChannelBitmap.getWidth();};
-
- int topBorder = viewport->getViewPositionY();
- int bottomBorder = viewport->getViewHeight() + topBorder;
-
- // clear appropriate section of the bitmap --
- // we need to do this before each channel draws its new section of data into lfpChannelBitmap
- Graphics gLfpChannelBitmap(lfpChannelBitmap);
- gLfpChannelBitmap.setColour(backgroundColour); //background color
-
- if (canvas->fullredraw)
- {
- gLfpChannelBitmap.fillRect(0,0, getWidth(), getHeight());
- } else {
- gLfpChannelBitmap.setColour(backgroundColour); //background color
-
- gLfpChannelBitmap.fillRect(fillfrom,0, (fillto-fillfrom)+1, getHeight());
- };
-
-
- for (int i = 0; i < numChans; i++)
-// for (int i = 0; i < drawableChannels.size(); ++i)
- {
-
- int componentTop = channels[i]->getY();
- int componentBottom = channels[i]->getHeight() + componentTop;
-
- if ((topBorder <= componentBottom && bottomBorder >= componentTop)) // only draw things that are visible
- {
- if (canvas->fullredraw)
- {
- channels[i]->fullredraw = true;
-
- channels[i]->pxPaint();
- channelInfo[i]->repaint();
-
- }
- else
- {
- channels[i]->pxPaint(); // draws to lfpChannelBitmap
-
- // it's not clear why, but apparently because the pxPaint() in a child component of LfpDisplay, we also need to issue repaint() calls for each channel, even though there's nothin to repaint there. Otherwise, the repaint call in LfpDisplay::refresh(), a few lines down, lags behind the update line by ~60 px. This could ahev something to do with teh reopaint message passing in juce. In any case, this seemingly redundant repaint here seems to fix the issue.
-
- // we redraw from 0 to +2 (px) relative to the real redraw window, the +1 draws the vertical update line
- channels[i]->repaint(fillfrom, 0, (fillto-fillfrom)+2, channels[i]->getHeight());
-
-
- }
- //std::cout << i << std::endl;
- }
-
- }
-
- if (fillfrom == 0 && singleChan != -1)
- {
- channelInfo[singleChan]->repaint();
- }
-
-
- if (canvas->fullredraw)
- {
- repaint(0,topBorder,getWidth(),bottomBorder-topBorder);
- }else{
- //repaint(fillfrom, topBorder, (fillto-fillfrom)+1, bottomBorder-topBorder); // doesntb seem to be needed and results in duplicate repaint calls
- }
-
- canvas->fullredraw = false;
-}
-
-
-
-void LfpDisplay::setRange(float r, DataChannel::DataChannelTypes type)
-{
- range[type] = r;
-
- if (channels.size() > 0)
- {
-
- for (int i = 0; i < numChans; i++)
- {
- if (channels[i]->getType() == type)
- channels[i]->setRange(range[type]);
- }
- canvas->fullredraw = true; //issue full redraw
- }
-}
-
-int LfpDisplay::getRange()
-{
- return getRange(options->getSelectedType());
-}
-
-int LfpDisplay::getRange(DataChannel::DataChannelTypes type)
-{
- for (int i=0; i < numChans; i++)
- {
- if (channels[i]->getType() == type)
- return channels[i]->getRange();
- }
- return 0;
-}
-
-
-void LfpDisplay::setChannelHeight(int r, bool resetSingle)
-{
- if (!getSingleChannelState()) cachedDisplayChannelHeight = r;
-
- for (int i = 0; i < numChans; i++)
- {
- channels[i]->setChannelHeight(r);
- channelInfo[i]->setChannelHeight(r);
- }
- if (resetSingle && singleChan != -1)
- {
- //std::cout << "width " << getWidth() << " numchans " << numChans << " height " << getChannelHeight() << std::endl;
- setSize(getWidth(),drawableChannels.size()*getChannelHeight());
- viewport->setScrollBarsShown(true,false);
- viewport->setViewPosition(Point(0,singleChan*r));
- singleChan = -1;
- for (int n = 0; n < numChans; n++)
- {
- channelInfo[n]->setEnabledState(savedChannelState[n]);
- }
- }
-
- resized();
-
-}
-
-void LfpDisplay::setInputInverted(bool isInverted)
-{
-
- for (int i = 0; i < numChans; i++)
- {
- channels[i]->setInputInverted(isInverted);
- }
-
- resized();
-
-}
-
-void LfpDisplay::setDrawMethod(bool isDrawMethod)
-{
- for (int i = 0; i < numChans; i++)
- {
- channels[i]->setDrawMethod(isDrawMethod);
- }
-
- if (isDrawMethod)
- {
- plotter = supersampledPlotter;
- }
- else
- {
- plotter = perPixelPlotter;
- }
-
- resized();
-
-}
-
-
-int LfpDisplay::getChannelHeight()
-{
-// return cachedDisplayChannelHeight;
- return drawableChannels[0].channel->getChannelHeight();
-// return channels[0]->getChannelHeight();
-}
-
-void LfpDisplay::cacheNewChannelHeight(int r)
-{
- cachedDisplayChannelHeight = r;
-}
-
-bool LfpDisplay::getChannelsReversed()
-{
- return channelsReversed;
-}
-
-void LfpDisplay::setChannelsReversed(bool state)
-{
- if (state == channelsReversed) return; // bail early, in case bookkeeping error
-
- channelsReversed = state;
-
- if (getSingleChannelState()) return; // don't reverse if single channel
-
- // reverse channels that are currently in drawableChannels
- for (int i = 0, j = drawableChannels.size() - 1, len = drawableChannels.size()/2;
- i < len;
- i++, j--)
- {
- // remove channel and info components from front and back
- // moving toward middle
- removeChildComponent(drawableChannels[i].channel);
- removeChildComponent(drawableChannels[j].channel);
- removeChildComponent(drawableChannels[i].channelInfo);
- removeChildComponent(drawableChannels[j].channelInfo);
-
- // swap front and back, moving towards middle
- drawableChannels.swap(i, j);
-
- // also swap coords
- {
- const auto channelBoundsA = drawableChannels[i].channel->getBounds();
- const auto channelInfoBoundsA = drawableChannels[i].channelInfo->getBounds();
-
- drawableChannels[i].channel->setBounds(drawableChannels[j].channel->getBounds());
- drawableChannels[i].channelInfo->setBounds(drawableChannels[j].channelInfo->getBounds());
- drawableChannels[j].channel->setBounds(channelBoundsA);
- drawableChannels[j].channelInfo->setBounds(channelInfoBoundsA);
- }
- }
-
-
- // remove middle component if odd number of channels
- if (drawableChannels.size() % 2 != 0)
- {
- removeChildComponent(drawableChannels[drawableChannels.size()/2+1].channel);
- removeChildComponent(drawableChannels[drawableChannels.size()/2+1].channelInfo);
- }
-
- // add the channels and channel info again
- for (int i = 0, len = drawableChannels.size(); i < len; i++)
- {
-
- if (!drawableChannels[i].channel->getHidden())
- {
- addAndMakeVisible(drawableChannels[i].channel);
- addAndMakeVisible(drawableChannels[i].channelInfo);
- }
-
- // flag this to update the waveforms
- drawableChannels[i].channel->fullredraw = true;
- }
-
- // necessary to overwrite lfpChannelBitmap's display
- refresh();
-}
-
-int LfpDisplay::getChannelDisplaySkipAmount()
-{
- return displaySkipAmt;
-}
-
-void LfpDisplay::setChannelDisplaySkipAmount(int skipAmt)
-{
- displaySkipAmt = skipAmt;
-
- if (!getSingleChannelState())
- rebuildDrawableChannelsList();
-
- canvas->redraw();
-}
-
-bool LfpDisplay::getMedianOffsetPlotting()
-{
- return m_MedianOffsetPlottingFlag;
-}
-
-void LfpDisplay::setMedianOffsetPlotting(bool isEnabled)
-{
- m_MedianOffsetPlottingFlag = isEnabled;
-}
-
-bool LfpDisplay::getSpikeRasterPlotting()
-{
- return m_SpikeRasterPlottingFlag;
-}
-
-void LfpDisplay::setSpikeRasterPlotting(bool isEnabled)
-{
- m_SpikeRasterPlottingFlag = isEnabled;
-}
-
-float LfpDisplay::getSpikeRasterThreshold()
-{
- return m_SpikeRasterThreshold;
-}
-
-void LfpDisplay::setSpikeRasterThreshold(float thresh)
-{
- m_SpikeRasterThreshold = thresh;
-}
-
-void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel)
-{
-
- //std::cout << "Mouse wheel " << e.mods.isCommandDown() << " " << wheel.deltaY << std::endl;
- //TODO Changing ranges with the wheel is currently broken. With multiple ranges, most
- //of the wheel range code needs updating
-
-
- if (e.mods.isCommandDown() && singleChan == -1) // CTRL + scroll wheel -> change channel spacing
- {
- int h = getChannelHeight();
- int hdiff=0;
-
- // std::cout << wheel.deltaY << std::endl;
-
- if (wheel.deltaY > 0)
- {
- hdiff = 2;
- }
- else
- {
- if (h > 5)
- hdiff = -2;
- }
-
- if (abs(h) > 100) // accelerate scrolling for large ranges
- hdiff *= 3;
-
- int newHeight = h+hdiff;
-
- // constrain the spread resizing to max and min values;
- if (newHeight < trackZoomInfo.minZoomHeight)
- {
- newHeight = trackZoomInfo.minZoomHeight;
- hdiff = 0;
- }
- else if (newHeight > trackZoomInfo.maxZoomHeight)
- {
- newHeight = trackZoomInfo.maxZoomHeight;
- hdiff = 0;
- }
-
- setChannelHeight(newHeight);
- int oldX=viewport->getViewPositionX();
- int oldY=viewport->getViewPositionY();
-
- setBounds(0,0,getWidth()-0, getChannelHeight()*drawableChannels.size()); // update height so that the scrollbar is correct
-
- int mouseY=e.getMouseDownY(); // should be y pos relative to inner viewport (0,0)
- int scrollBy = (mouseY/h)*hdiff*2;// compensate for motion of point under current mouse position
- viewport->setViewPosition(oldX,oldY+scrollBy); // set back to previous position plus offset
-
- options->setSpreadSelection(newHeight); // update combobox
-
- canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw
- }
- else
- {
- if (e.mods.isAltDown()) // ALT + scroll wheel -> change channel range (was SHIFT but that clamps wheel.deltaY to 0 on OSX for some reason..)
- {
- int h = getRange();
-
-
- int step = options->getRangeStep(options->getSelectedType());
-
- // std::cout << wheel.deltaY << std::endl;
-
- if (wheel.deltaY > 0)
- {
- setRange(h+step,options->getSelectedType());
- }
- else
- {
- if (h > step+1)
- setRange(h-step,options->getSelectedType());
- }
-
- options->setRangeSelection(h); // update combobox
- canvas->fullredraw = true; //issue full redraw - scrolling without modifier doesnt require a full redraw
-
- }
- else // just scroll
- {
- // passes the event up to the viewport so the screen scrolls
- if (viewport != nullptr && e.eventComponent == this) // passes only if it's not a listening event
- viewport->mouseWheelMove(e.getEventRelativeTo(canvas), wheel);
-
- }
-
-
- }
- //refresh(); // doesn't seem to be needed now that channels daraw to bitmap
-
-}
-
-void LfpDisplay::toggleSingleChannel(int chan)
-{
- if (!getSingleChannelState())
- {
-
- std::cout << "Single channel on (" << chan << ")" << std::endl;
- singleChan = chan;
-
- int newHeight = viewport->getHeight();
- LfpChannelTrack lfpChannelTrack{drawableChannels[chan].channel, drawableChannels[chan].channelInfo};
- lfpChannelTrack.channelInfo->setEnabledState(true);
- lfpChannelTrack.channelInfo->setSingleChannelState(true);
-
- removeAllChildren();
-
- // disable unused channels
- for (int i = 0; i < getNumChannels(); i++)
- {
- if (i != chan)
- {
- drawableChannels[i].channel->setEnabledState(false);
- }
- }
-
- // update drawableChannels, give only the single channel to focus on
- drawableChannels.clearQuick();
- drawableChannels.add(lfpChannelTrack);
-
- addAndMakeVisible(lfpChannelTrack.channel);
- addAndMakeVisible(lfpChannelTrack.channelInfo);
-
- // set channel height and position (so that we allocate the smallest
- // necessary image size for drawing)
- setChannelHeight(newHeight, false);
-
- lfpChannelTrack.channel->setTopLeftPosition(canvas->leftmargin, 0);
- lfpChannelTrack.channelInfo->setTopLeftPosition(0, 0);
- setSize(getWidth(), getChannelHeight());
-
- viewport->setViewPosition(0, 0);
-
- }
-// else if (chan == singleChan || chan == -2)
- else
- {
- std::cout << "Single channel off" << std::endl;
- for (int n = 0; n < numChans; n++)
- {
- channelInfo[n]->setSingleChannelState(false);
- }
-
- setChannelHeight(cachedDisplayChannelHeight);
-
- reactivateChannels();
- rebuildDrawableChannelsList();
- }
-}
-
-void LfpDisplay::reactivateChannels()
-{
-
- for (int n = 0; n < channels.size(); n++)
- setEnabledState(savedChannelState[n], n);
-
-}
-
-void LfpDisplay::rebuildDrawableChannelsList()
-{
-
- if (displaySkipAmt != 0) removeAllChildren(); // start with clean slate
-
- Array channelsToDraw;
- drawableChannels = Array();
-
- // iterate over all channels and select drawable ones
- for (int i = 0, drawableChannelNum = 0; i < channels.size(); i++)
- {
-// std::cout << "\tchannel " << i << " has subprocessor index of " << channelInfo[i]->getSubprocessorIdx() << std::endl;
- // if channel[i] is not sourced from the correct subprocessor, then hide it and continue
- //if (channelInfo[i]->getSubprocessorIdx() != getDisplayedSubprocessor())
- //{
- // channels[i]->setHidden(true);
- // channelInfo[i]->setHidden(true);
- // continue;
- //}
-
- //std::cout << "Checking for hidden channels" << std::endl;
- if (displaySkipAmt == 0 || (i % displaySkipAmt == 0)) // no skips, add all channels
- {
- channels[i]->setHidden(false);
- channelInfo[i]->setHidden(false);
-
- channelInfo[i]->setDrawableChannelNumber(drawableChannelNum++);
- channelInfo[i]->resized(); // to update the conditional drawing of enableButton and channel num
-
- channelsToDraw.add(LfpDisplay::LfpChannelTrack{
- channels[i],
- channelInfo[i]
- });
-
- addAndMakeVisible(channels[i]);
- addAndMakeVisible(channelInfo[i]);
- }
- else // skip some channels
- {
-// if (i % (displaySkipAmt) == 0) // add these channels
-// {
-// channels[i]->setHidden(false);
-// channelInfo[i]->setHidden(false);
-//
-// channelsToDraw.add(LfpDisplay::LfpChannelTrack{
-// channels[i],
-// channelInfo[i]
-// });
-//
-// addAndMakeVisible(channels[i]);
-// addAndMakeVisible(channelInfo[i]);
-// }
-// else // but not these
-// {
- channels[i]->setHidden(true);
- channelInfo[i]->setHidden(true);
-
- removeChildComponent(channels[i]);
- removeChildComponent(channelInfo[i]);
-// }
- }
- }
-
- // check if channels should be added to drawableChannels in reverse
- if (getChannelsReversed())
- {
- for (int i = channelsToDraw.size() - 1; i >= 0; --i)
- {
- drawableChannels.add(channelsToDraw[i]);
- }
- }
- else
- {
- for (int i = 0; i < channelsToDraw.size(); ++i)
- {
- drawableChannels.add(channelsToDraw[i]);
- }
- }
-
- // this guards against an exception where the editor sets the drawable samplerate
- // before the lfpDisplay is fully initialized
- if (getHeight() > 0 && getWidth() > 0)
- {
- canvas->resizeToChannels();
- }
-
- setColors();
-}
-
-LfpBitmapPlotter * const LfpDisplay::getPlotterPtr() const
-{
- return plotter;
-}
-
-bool LfpDisplay::getSingleChannelState()
-{
- //if (singleChan < 0) return false;
- //else return true;
- return singleChan >= 0;
-}
-
-
-void LfpDisplay::mouseDown(const MouseEvent& event)
-{
- if (drawableChannels.isEmpty())
- {
- return;
- }
-
- //int y = event.getMouseDownY(); //relative to each channel pos
- MouseEvent canvasevent = event.getEventRelativeTo(viewport);
- int y = canvasevent.getMouseDownY() + viewport->getViewPositionY(); // need to account for scrolling
- int x = canvasevent.getMouseDownX();
-
- int dist = 0;
- int mindist = 10000;
- int closest = 5;
- for (int n = 0; n < drawableChannels.size(); n++) // select closest instead of relying on eventComponent
- {
- drawableChannels[n].channel->deselect();
-
- int cpos = (drawableChannels[n].channel->getY() + (drawableChannels[n].channel->getHeight()/2));
- dist = int(abs(y - cpos));
-
-// std::cout << "Mouse down at " << y << " pos is "<< cpos << " n: " << n << " dist " << dist << std::endl;
-
- if (dist < mindist)
- {
- mindist = dist-1;
- closest = n;
- }
- }
-
- drawableChannels[closest].channel->select();
- options->setSelectedType(drawableChannels[closest].channel->getType());
-
- if (event.mods.isRightButtonDown()) { // if right click
- PopupMenu channelMenu = channels[closest]->getOptions();
- const int result = channelMenu.show();
- drawableChannels[closest].channel->changeParameter(result);
- }
- else // if left click
- {
-// if (singleChan != -1)
- if (event.getNumberOfClicks() == 2) {
- toggleSingleChannel(closest);
- }
-
- if (getSingleChannelState())
- {
-
- // std::cout << "singleChan = " << singleChan << " " << y << " " << drawableChannels[0].channel->getHeight() << " " << getRange() << std::endl;
- //channelInfo[singleChan]->updateXY(
- drawableChannels[0].channelInfo->updateXY(
- float(x)/getWidth()*canvas->timebase,
- (-(float(y)-viewport->getViewPositionY())/viewport->getViewHeight()*float(getRange()))+float(getRange()/2)
- );
- }
- }
-
-// canvas->fullredraw = true;//issue full redraw
-
-// refresh();
-
-}
-
-
-bool LfpDisplay::setEventDisplayState(int ch, bool state)
-{
- eventDisplayEnabled[ch] = state;
- return eventDisplayEnabled[ch];
-}
-
-
-bool LfpDisplay::getEventDisplayState(int ch)
-{
- return eventDisplayEnabled[ch];
-}
-
-
-void LfpDisplay::setEnabledState(bool state, int chan, bool updateSaved)
-{
- if (chan < numChans)
- {
- channels[chan]->setEnabledState(state);
- channelInfo[chan]->setEnabledState(state);
-
- if (updateSaved)
- savedChannelState.set(chan, state);
-
- canvas->isChannelEnabled.set(chan, state);
- }
-}
-
-bool LfpDisplay::getEnabledState(int chan)
-{
- if (chan < numChans)
- {
- return channels[chan]->getEnabledState();
- }
-
- return false;
-}
-
-
-
-#pragma mark - LfpChannelDisplay -
-// ------------------------------------------------------------------
-
-LfpChannelDisplay::LfpChannelDisplay(LfpDisplayCanvas* c, LfpDisplay* d, LfpDisplayOptions* o, int channelNumber)
- : canvas(c)
- , display(d)
- , options(o)
- , isSelected(false)
- , chan(channelNumber)
- , name("")
- , drawableChan(channelNumber)
- , channelOverlap(300)
- , channelHeight(30)
- , range(250.0f)
- , isEnabled(true)
- , inputInverted(false)
- , canBeInverted(true)
- , drawMethod(false)
- , isHidden(false)
-{
-
-
- name = String(channelNumber+1); // default is to make the channelNumber the name
-
-
- channelHeightFloat = (float) channelHeight;
-
- channelFont = Font("Default", channelHeight*0.6, Font::plain);
-
- lineColour = Colour(255,255,255);
-
- type = options->getChannelType(channelNumber);
- typeStr = options->getTypeName(type);
-
-}
-
-LfpChannelDisplay::~LfpChannelDisplay()
-{
-
-}
-
-void LfpChannelDisplay::resized()
-{
- // all of this will likely need to be moved into the sharedLfpDisplay image in the lfpDisplay, not here
- // now that the complete height is know, the sharedLfpDisplay image that we'll draw the pixel-wise lfp plot to needs to be resized
- //lfpChannelBitmap = Image(Image::ARGB, getWidth(), getHeight(), false);
-}
-
-
-void LfpChannelDisplay::updateType()
-{
- type = options->getChannelType(chan);
- typeStr = options->getTypeName(type);
-}
-
-void LfpChannelDisplay::setEnabledState(bool state)
-{
-
- //if (state)
- //std::cout << "Setting channel " << name << " to true." << std::endl;
- //else
- //std::cout << "Setting channel " << name << " to false." << std::endl;
-
- isEnabled = state;
-
-}
-
-void LfpChannelDisplay::setHidden(bool isHidden_)
-{
- isHidden = isHidden_;
- isEnabled = !isHidden;
-}
-
-void LfpChannelDisplay::pxPaint()
-{
- if (!isEnabled) return; // return early if THIS display is not enabled
-
- Image::BitmapData bdLfpChannelBitmap(display->lfpChannelBitmap, 0,0, display->lfpChannelBitmap.getWidth(), display->lfpChannelBitmap.getHeight());
-
- int center = getHeight()/2;
-
- // max and min of channel in absolute px coords for event displays etc - actual data might be drawn outside of this range
- int jfrom_wholechannel= (int) (getY()+center-channelHeight/2)+1 +0 ;
- int jto_wholechannel= (int) (getY()+center+channelHeight/2) -0;
-
- //int jfrom_wholechannel_almost= (int) (getY()+center-channelHeight/3)+1 +0 ; // a bit less tall, for saturation warnings
- //int jto_wholechannel_almost= (int) (getY()+center+channelHeight/3) -0;
-
- // max and min of channel, this is the range where actual data is drawn
- int jfrom_wholechannel_clip= (int) (getY()+center-(channelHeight)*canvas->channelOverlapFactor)+1 ;
- int jto_wholechannel_clip = (int) (getY()+center+(channelHeight)*canvas->channelOverlapFactor) -0;
-
- if (jfrom_wholechannel<0) {jfrom_wholechannel=0;};
- if (jto_wholechannel >= display->lfpChannelBitmap.getHeight()) {jto_wholechannel=display->lfpChannelBitmap.getHeight()-1;};
-
- // draw most recent drawn sample position
- if (canvas->screenBufferIndex[chan]+1 <= display->lfpChannelBitmap.getWidth())
- for (int k=jfrom_wholechannel; k<=jto_wholechannel; k+=2) // draw line
- bdLfpChannelBitmap.setPixelColour(canvas->screenBufferIndex[chan]+1,k, Colours::yellow);
-
-
- bool clipWarningHi =false; // keep track if something clipped in the display, so we can draw warnings after the data pixels are done
- bool clipWarningLo =false;
-
- bool saturateWarningHi =false; // similar, but for saturating the amplifier, not just the display - make this warning very visible
- bool saturateWarningLo =false;
-
- // pre compute some colors for later so we dont do it once per pixel.
- Colour lineColourBright = lineColour.withMultipliedBrightness(2.0f);
- //Colour lineColourDark = lineColour.withMultipliedSaturation(0.5f).withMultipliedBrightness(0.3f);
- Colour lineColourDark = lineColour.withMultipliedSaturation(0.5f*canvas->histogramParameterB).withMultipliedBrightness(canvas->histogramParameterB);
-
-
- int stepSize = 1;
- int from = 0; // for vertical line drawing in the LFP data
- int to = 0;
-
- int ifrom = canvas->lastScreenBufferIndex[chan] - 1; // need to start drawing a bit before the actual redraw window for the interpolated line to join correctly
-
- if (ifrom < 0)
- ifrom = 0;
-
- int ito = canvas->screenBufferIndex[chan] +0;
-
- if (fullredraw)
- {
- ifrom = 0; //canvas->leftmargin;
- ito = getWidth()-stepSize;
- fullredraw = false;
- }
-
- bool drawWithOffsetCorrection = display->getMedianOffsetPlotting();
-
- LfpBitmapPlotterInfo plotterInfo; // hold and pass plotting info for each plotting method class
-
-
- for (int i = ifrom; i < ito ; i += stepSize) // redraw only changed portion
- {
- if (i < display->lfpChannelBitmap.getWidth())
- {
- //draw zero line
- int m = getY()+center;
-
- if(m > 0 && m < display->lfpChannelBitmap.getHeight())
- {
- if ( bdLfpChannelBitmap.getPixelColour(i,m) == display->backgroundColour ) { // make sure we're not drawing over an existing plot from another channel
- bdLfpChannelBitmap.setPixelColour(i,m,Colour(50,50,50));
- }
- }
-
- //draw range markers
- if (isSelected)
- {
- int start = getY()+center -channelHeight/2;
- int jump = channelHeight/4;
-
- for (m = start; m <= start + jump*4; m += jump)
- {
- if (m > 0 && m < display->lfpChannelBitmap.getHeight())
- {
- if ( bdLfpChannelBitmap.getPixelColour(i,m) == display->backgroundColour ) // make sure we're not drawing over an existing plot from another channel
- bdLfpChannelBitmap.setPixelColour(i, m, Colour(80,80,80));
- }
- }
- }
-
- // draw event markers
- int rawEventState = canvas->getYCoord(canvas->getNumChannels(), i);// get last channel+1 in buffer (represents events)
-
- for (int ev_ch = 0; ev_ch < 8 ; ev_ch++) // for all event channels
- {
- if (display->getEventDisplayState(ev_ch)) // check if plotting for this channel is enabled
- {
- if (rawEventState & (1 << ev_ch)) // events are representet by a bit code, so we have to extract the individual bits with a mask
- {
-// std::cout << "Drawing event." << std::endl;
- Colour currentcolor=display->channelColours[ev_ch*2];
-
- for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++) // draw line
- bdLfpChannelBitmap.setPixelColour(i,k,bdLfpChannelBitmap.getPixelColour(i,k).interpolatedWith(currentcolor,0.3f));
-
- }
- }
- }
-
- //std::cout << "e " << canvas->getYCoord(canvas->getNumChannels()-1, i) << std::endl;
-
-
- // set max-min range for plotting, used in all methods
- double a = (canvas->getYCoordMax(chan, i)/range*channelHeightFloat);
- double b = (canvas->getYCoordMin(chan, i)/range*channelHeightFloat);
-
- double mean = (canvas->getMean(chan)/range*channelHeightFloat);
-
- if (drawWithOffsetCorrection)
- {
- a -= mean;
- b -= mean;
- }
-
- double a_raw = canvas->getYCoordMax(chan, i);
- double b_raw = canvas->getYCoordMin(chan, i);
- double from_raw=0; double to_raw=0;
-
- //double m = (canvas->getYCoordMean(chan, i)/range*channelHeightFloat)+getHeight()/2;
- if (achannelOverlapFactor;
- if (lm>0)
- lm=-lm;
-
- if (from > -lm) {from = -lm; clipWarningHi=true;};
- if (to > -lm) {to = -lm; clipWarningHi=true;};
- if (from < lm) {from = lm; clipWarningLo=true;};
- if (to < lm) {to = lm; clipWarningLo=true;};
-
-
- // test if raw data is clipped for displaying saturation warning
- if (from_raw > options->selectedSaturationValueFloat) { saturateWarningHi=true;};
- if (to_raw > options->selectedSaturationValueFloat) { saturateWarningHi=true;};
- if (from_raw < -options->selectedSaturationValueFloat) { saturateWarningLo=true;};
- if (to_raw < -options->selectedSaturationValueFloat) { saturateWarningLo=true;};
-
- bool spikeFlag = display->getSpikeRasterPlotting()
- && !(saturateWarningHi || saturateWarningLo)
- && (from_raw - canvas->getYCoordMean(chan, i) < display->getSpikeRasterThreshold()
- || to_raw - canvas->getYCoordMean(chan, i) < display->getSpikeRasterThreshold());
-
- from = from + getHeight()/2; // so the plot is centered in the channeldisplay
- to = to + getHeight()/2;
-
- int samplerange = to - from;
-
- if (drawMethod) // switched between 'supersampled' drawing and simple pixel wise drawing
- { // histogram based supersampling method
- plotterInfo.channelID = chan;
- plotterInfo.samp = i;
- plotterInfo.y = getY();
- plotterInfo.from = from;
- plotterInfo.height = getHeight();
- plotterInfo.lineColourBright = lineColourBright;
- plotterInfo.lineColourDark = lineColourDark;
- plotterInfo.range = range;
- plotterInfo.channelHeightFloat = channelHeightFloat;
- plotterInfo.sampleCountPerPixel = canvas->getSampleCountPerPixel(i);
- plotterInfo.samplesPerPixel = canvas->getSamplesPerPixel(chan, i);
- plotterInfo.histogramParameterA = canvas->histogramParameterA;
- plotterInfo.samplerange = samplerange;
-
- // TODO: (kelly) complete transition toward plotter class encapsulation
-// display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo);
-
- }
- else //drawmethod
- { // simple per-pixel min-max drawing, has no anti-aliasing, but runs faster
-
- plotterInfo.channelID = chan;
- plotterInfo.y = getY();
- plotterInfo.from = from;
- plotterInfo.to = to;
- plotterInfo.samp = i;
- plotterInfo.lineColour = lineColour;
-
- // TODO: (kelly) complete transition toward plotter class encapsulation
-// display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo); // plotterInfo is prepared above
-
- }
-
- // Do the actual plotting for the selected plotting method
- if (!display->getSpikeRasterPlotting())
- display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo);
-
-
-
-
- // now draw warnings, if needed
- if (canvas->drawClipWarning) // draw simple warning if display cuts off data
- {
-
- if(clipWarningHi) {
- for (int j=0; j<=3; j++)
- {
- int clipmarker = jto_wholechannel_clip;
-
- if(clipmarker>0 && clipmarkerlfpChannelBitmap.getHeight()){
- bdLfpChannelBitmap.setPixelColour(i,clipmarker-j,Colour(255,255,255));
- }
- }
- }
-
- if(clipWarningLo) {
- for (int j=0; j<=3; j++)
- {
- int clipmarker = jfrom_wholechannel_clip;
-
- if(clipmarker>0 && clipmarkerlfpChannelBitmap.getHeight()){
- bdLfpChannelBitmap.setPixelColour(i,clipmarker+j,Colour(255,255,255));
- }
- }
- }
-
- clipWarningHi=false;
- clipWarningLo=false;
- }
-
- if (spikeFlag) // draw spikes
- {
- for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++){ // draw line
- if(k>0 && klfpChannelBitmap.getHeight()){
- bdLfpChannelBitmap.setPixelColour(i,k,lineColour);
- }
- };
- }
-
-
- if (canvas->drawSaturationWarning) // draw bigger warning if actual data gets cuts off
- {
-
- if(saturateWarningHi || saturateWarningLo) {
-
-
- for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++){ // draw line
- Colour thiscolour=Colour(255,0,0);
- if (fmod((i+k),50)>25){
- thiscolour=Colour(255,255,255);
- }
- if(k>0 && klfpChannelBitmap.getHeight()){
- bdLfpChannelBitmap.setPixelColour(i,k,thiscolour);
- }
- };
- }
-
- saturateWarningHi=false; // we likely just need one of this because for this warning we dont care if its saturating on the positive or negative side
- saturateWarningLo=false;
- }
- } // if i < getWidth()
-
- } // for i (x pixels)
-
-}
-
-void LfpChannelDisplay::paint(Graphics& g) {}
-
-
-
-PopupMenu LfpChannelDisplay::getOptions()
-{
-
- PopupMenu menu;
- menu.addItem(1, "Invert signal", true, inputInverted);
-
- return menu;
-}
-
-void LfpChannelDisplay::changeParameter(int id)
-{
- switch (id)
- {
- case 1:
- setInputInverted(!inputInverted);
- default:
- break;
- }
-}
-
-void LfpChannelDisplay::setRange(float r)
-{
-
- range = r;
-
- //std::cout << "Range: " << r << std::endl;
-}
-
-int LfpChannelDisplay::getRange()
-{
- return range;
-}
-
-
-void LfpChannelDisplay::select()
-{
- isSelected = true;
-}
-
-void LfpChannelDisplay::deselect()
-{
- isSelected = false;
-}
-
-bool LfpChannelDisplay::getSelected()
-{
- return isSelected;
-}
-
-void LfpChannelDisplay::setColour(Colour c)
-{
- lineColour = c;
-}
-
-
-void LfpChannelDisplay::setChannelHeight(int c)
-{
- channelHeight = c;
-
- channelHeightFloat = (float) channelHeight;
-
- if (!inputInverted)
- channelHeightFloat = -channelHeightFloat;
-
- channelOverlap = channelHeight*2;
-}
-
-int LfpChannelDisplay::getChannelHeight()
-{
-
- return channelHeight;
-}
-
-void LfpChannelDisplay::setChannelOverlap(int overlap)
-{
- channelOverlap = overlap;
-}
-
-
-int LfpChannelDisplay::getChannelOverlap()
-{
- return channelOverlap;
-}
-
-int LfpChannelDisplay::getChannelNumber()
-{
- return chan;
-}
-
-String LfpChannelDisplay::getName()
-{
- return name;
-}
-
-int LfpChannelDisplay::getDrawableChannelNumber()
-{
- return drawableChan;
-}
-
-void LfpChannelDisplay::setDrawableChannelNumber(int channelId)
-{
- drawableChan = channelId;
-}
-
-void LfpChannelDisplay::setCanBeInverted(bool _canBeInverted)
-{
- canBeInverted = _canBeInverted;
-}
-
-void LfpChannelDisplay::setInputInverted(bool isInverted)
-{
- if (canBeInverted)
- {
- inputInverted = isInverted;
- setChannelHeight(channelHeight);
- }
-}
-
-void LfpChannelDisplay::setDrawMethod(bool isDrawMethod)
-{
-
- drawMethod = isDrawMethod;
-
-}
-
-
-void LfpChannelDisplay::setName(String name_)
-{
- name = name_;
-}
-
-DataChannel::DataChannelTypes LfpChannelDisplay::getType()
-{
- return type;
-}
-
-
-
-#pragma mark - LfpChannelDisplayInfo -
-// -------------------------------
-
-LfpChannelDisplayInfo::LfpChannelDisplayInfo(LfpDisplayCanvas* canvas_, LfpDisplay* display_, LfpDisplayOptions* options_, int ch)
- : LfpChannelDisplay(canvas_, display_, options_, ch)
-{
-
- chan = ch;
- x = -1.0f;
- y = -1.0f;
-
-// enableButton = new UtilityButton(String(ch+1), Font("Small Text", 13, Font::plain));
- enableButton = new UtilityButton("", Font("Small Text", 13, Font::plain));
- enableButton->setRadius(5.0f);
-
- enableButton->setEnabledState(true);
- enableButton->setCorners(true, true, true, true);
- enableButton->addListener(this);
- enableButton->setClickingTogglesState(true);
- enableButton->setToggleState(true, dontSendNotification);
-
- isSingleChannel = false;
-
- addAndMakeVisible(enableButton);
-
-}
-
-void LfpChannelDisplayInfo::updateType()
-{
- type = options->getChannelType(chan);
- typeStr = options->getTypeName(type);
- repaint();
-}
-
-void LfpChannelDisplayInfo::buttonClicked(Button* button)
-{
-
- bool state = button->getToggleState();
-
- display->setEnabledState(state, chan);
-
- //UtilityButton* b = (UtilityButton*) button;
-
- // if (state)
- // {
- // b->setLabel("ON");
- // } else {
- // b->setLabel("OFF");
- // }
-
- //std::cout << "Turn channel " << chan << " to " << button->getToggleState() << std::endl;
-
-}
-
-void LfpChannelDisplayInfo::setEnabledState(bool state)
-{
- enableButton->setToggleState(state, sendNotification);
-}
-
-void LfpChannelDisplayInfo::setSingleChannelState(bool state)
-{
- isSingleChannel = state;
-}
-
-int LfpChannelDisplayInfo::getChannelSampleRate()
-{
- return samplerate;
-}
-
-void LfpChannelDisplayInfo::setChannelSampleRate(int samplerate_)
-{
- samplerate = samplerate_;
-}
-
-void LfpChannelDisplayInfo::mouseDrag(const MouseEvent &e)
-{
- if (e.mods.isLeftButtonDown()) // double check that we initiate only for left click and hold
- {
- if (e.mods.isCommandDown() && !display->getSingleChannelState()) // CTRL + drag -> change channel spacing
- {
-
- // init state in our track zooming info struct
- if (!display->trackZoomInfo.isScrollingY)
- {
- auto & zoomInfo = display->trackZoomInfo;
-
- zoomInfo.isScrollingY = true;
- zoomInfo.componentStartHeight = getChannelHeight();
- zoomInfo.zoomPivotRatioY = (getY() + e.getMouseDownY())/(float)display->getHeight();
- zoomInfo.zoomPivotRatioX = (getX() + e.getMouseDownX())/(float)display->getWidth();
- zoomInfo.zoomPivotViewportOffset = getPosition() + e.getMouseDownPosition() - canvas->viewport->getViewPosition();
-
- zoomInfo.unpauseOnScrollEnd = !display->isPaused;
- if (!display->isPaused) display->options->togglePauseButton(true);
- }
-
- int h = display->trackZoomInfo.componentStartHeight;
- int hdiff=0;
- int dragDeltaY = -0.1 * (e.getScreenPosition().getY() - e.getMouseDownScreenY()); // invert so drag up -> scale up
-
-// std::cout << dragDeltaY << std::endl;
- if (dragDeltaY > 0)
- {
- hdiff = 2 * dragDeltaY;
- }
- else
- {
- if (h > 5)
- hdiff = 2 * dragDeltaY;
- }
-
- if (abs(h) > 100) // accelerate scrolling for large ranges
- hdiff *= 3;
-
- int newHeight = h+hdiff;
-
- // constrain the spread resizing to max and min values;
- if (newHeight < display->trackZoomInfo.minZoomHeight)
- {
- newHeight = display->trackZoomInfo.minZoomHeight;
- }
- else if (newHeight > display->trackZoomInfo.maxZoomHeight)
- {
- newHeight = display->trackZoomInfo.maxZoomHeight;
- }
-
- // return early if there is nothing to update
- if (newHeight == getChannelHeight())
- {
- return;
- }
-
- // set channel heights for all channel
-// display->setChannelHeight(newHeight);
- for (int i = 0; i < display->getNumChannels(); ++i)
- {
- display->channels[i]->setChannelHeight(newHeight);
- display->channelInfo[i]->setChannelHeight(newHeight);
- }
-
- options->setSpreadSelection(newHeight, false, true); // update combobox
-
- canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw
-
- display->setBounds(0,0,display->getWidth()-0, display->getChannelHeight()*display->drawableChannels.size()); // update height so that the scrollbar is correct
-
- int newViewportY = display->trackZoomInfo.zoomPivotRatioY * display->getHeight() - display->trackZoomInfo.zoomPivotViewportOffset.getY();
- if (newViewportY < 0) newViewportY = 0; // make sure we don't adjust beyond the edge of the actual view
-
- canvas->viewport->setViewPosition(0, newViewportY);
- }
- }
-}
-
-void LfpChannelDisplayInfo::mouseUp(const MouseEvent &e)
-{
- if (e.mods.isLeftButtonDown() && display->trackZoomInfo.isScrollingY)
- {
- display->trackZoomInfo.isScrollingY = false;
- if (display->trackZoomInfo.unpauseOnScrollEnd)
- {
- display->isPaused = false;
- display->options->togglePauseButton(false);
- }
- }
-}
-
-void LfpChannelDisplayInfo::paint(Graphics& g)
-{
-
- int center = getHeight()/2 - (isSingleChannel?(75):(0));
- const bool showChannelNumbers = options->getChannelNameState();
-
- // Draw the channel numbers
- g.setColour(Colours::grey);
- const String channelString = (isChannelNumberHidden() ? ("--") :
- showChannelNumbers ? String(getChannelNumber() + 1) : getName());
- bool isCentered = !getEnabledButtonVisibility();
-
- g.drawText(channelString,
- showChannelNumbers ? 6 : 2,
- center-4,
- getWidth()/2,
- 10,
- isCentered ? Justification::centred : Justification::centredLeft,
- false);
-
- g.setColour(lineColour);
- g.fillRect(0, 0, 2, getHeight());
-
- if (getChannelTypeStringVisibility())
- {
- g.setFont(Font("Small Text", 13, Font::plain));
- g.drawText(typeStr,5,center+10,41,10,Justification::centred,false);
- }
- // g.setFont(channelHeightFloat*0.3);
- g.setFont(Font("Small Text", 11, Font::plain));
-
- if (isSingleChannel)
- {
- g.setColour(Colours::darkgrey);
- g.drawText("STD:", 5, center+90,41,10,Justification::centred,false);
- g.drawText("MEAN:", 5, center+40,41,10,Justification::centred,false);
-
- if (x > 0)
- {
- g.drawText("uV:", 5, center+140,41,10,Justification::centred,false);
- }
- //g.drawText("Y:", 5, center+200,41,10,Justification::centred,false);
-
- g.setColour(Colours::grey);
- g.drawText(String(canvas->getStd(chan)), 5, center+110,41,10,Justification::centred,false);
- g.drawText(String(canvas->getMean(chan)), 5, center+60,41,10,Justification::centred,false);
- if (x > 0)
- {
- //g.drawText(String(x), 5, center+150,41,10,Justification::centred,false);
- g.drawText(String(y), 5, center+160,41,10,Justification::centred,false);
- }
-
- }
-
- // g.drawText(name, 10, center-channelHeight/2, 200, channelHeight, Justification::left, false);
-
-}
-
-void LfpChannelDisplayInfo::updateXY(float x_, float y_)
-{
- x = x_;
- y = y_;
-}
-
-void LfpChannelDisplayInfo::resized()
-{
-
- int center = getHeight()/2 - (isSingleChannel?(75):(0));
- setEnabledButtonVisibility(getHeight() >= 16);
-
- if (getEnabledButtonVisibility())
- {
- enableButton->setBounds(getWidth()/2 - 10, center - 5, 10, 10);
- }
-
- setChannelNumberIsHidden(getHeight() < 16 && (getDrawableChannelNumber() + 1) % 10 != 0);
-
- setChannelTypeStringVisibility(getHeight() > 34);
-}
-
-void LfpChannelDisplayInfo::setEnabledButtonVisibility(bool shouldBeVisible)
-{
- if (shouldBeVisible)
- {
- addAndMakeVisible(enableButton);
- }
- else if (enableButton->isVisible())
- {
- removeChildComponent(enableButton);
- enableButton->setVisible(false);
- }
-
-}
-
-bool LfpChannelDisplayInfo::getEnabledButtonVisibility()
-{
- return enableButton->isVisible();
-}
-
-void LfpChannelDisplayInfo::setChannelTypeStringVisibility(bool shouldBeVisible)
-{
- channelTypeStringIsVisible = shouldBeVisible;
-}
-
-bool LfpChannelDisplayInfo::getChannelTypeStringVisibility()
-{
- return channelTypeStringIsVisible || isSingleChannel;
-}
-
-void LfpChannelDisplayInfo::setChannelNumberIsHidden(bool shouldBeHidden)
-{
- channelNumberHidden = shouldBeHidden;
-}
-
-bool LfpChannelDisplayInfo::isChannelNumberHidden()
-{
- return channelNumberHidden;
-}
-
-
-
-
-#pragma mark - EventDisplayInterface -
-// Event display Options --------------------------------------------------------------------
-
-EventDisplayInterface::EventDisplayInterface(LfpDisplay* display_, LfpDisplayCanvas* canvas_, int chNum):
- isEnabled(true), display(display_), canvas(canvas_)
-{
-
- channelNumber = chNum;
-
- chButton = new UtilityButton(String(channelNumber+1), Font("Small Text", 13, Font::plain));
- chButton->setRadius(5.0f);
- chButton->setBounds(4,4,14,14);
- chButton->setEnabledState(true);
- chButton->setCorners(true, false, true, false);
- //chButton.color = display->channelColours[channelNumber*2];
- chButton->addListener(this);
- addAndMakeVisible(chButton);
-
-
- checkEnabledState();
-
-}
-
-EventDisplayInterface::~EventDisplayInterface()
-{
-
-}
-
-void EventDisplayInterface::checkEnabledState()
-{
- isEnabled = display->getEventDisplayState(channelNumber);
-
- //repaint();
-}
-
-void EventDisplayInterface::buttonClicked(Button* button)
-{
- checkEnabledState();
- if (isEnabled)
- {
- display->setEventDisplayState(channelNumber, false);
- }
- else
- {
- display->setEventDisplayState(channelNumber, true);
- }
-
- repaint();
-
-}
-
-
-void EventDisplayInterface::paint(Graphics& g)
-{
-
- checkEnabledState();
-
- if (isEnabled)
- {
- g.setColour(display->channelColours[channelNumber*2]);
- g.fillRoundedRectangle(2,2,18,18,6.0f);
- }
-
-
- //g.drawText(String(channelNumber), 8, 2, 200, 15, Justification::left, false);
-
-}
-
-
-
-#pragma mark - LfpViewport -
-// Lfp Viewport -------------------------------------------
-
-LfpViewport::LfpViewport(LfpDisplayCanvas *canvas)
- : Viewport()
-{
- this->canvas = canvas;
-}
-
-void LfpViewport::visibleAreaChanged(const Rectangle& newVisibleArea)
-{
- canvas->fullredraw = true;
- canvas->refresh();
-}
-
-
-
-#pragma mark - PerPixelBitmapPlotter -
-
-PerPixelBitmapPlotter::PerPixelBitmapPlotter(LfpDisplay * lfpDisplay)
- : LfpBitmapPlotter(lfpDisplay)
-{ }
-
-void PerPixelBitmapPlotter::plot(Image::BitmapData &bitmapData, LfpBitmapPlotterInfo &pInfo)
-{
- int jfrom = pInfo.from + pInfo.y;
- int jto = pInfo.to + pInfo.y;
-
- //if (yofs<0) {yofs=0;};
-
- if (pInfo.samp < 0) {pInfo.samp = 0;};
- if (pInfo.samp >= display->lfpChannelBitmap.getWidth()) {pInfo.samp = display->lfpChannelBitmap.getWidth()-1;}; // this shouldnt happen, there must be some bug above - to replicate, run at max refresh rate where draws overlap the right margin by a lot
-
- if (jfrom<0) {jfrom=0;};
- if (jto >= display->lfpChannelBitmap.getHeight()) {jto=display->lfpChannelBitmap.getHeight()-1;};
-
-
- for (int j = jfrom; j <= jto; j += 1)
- {
-
- //uint8* const pu8Pixel = bdSharedLfpDisplay.getPixelPointer( (int)(i),(int)(j));
- //*(pu8Pixel) = 200;
- //*(pu8Pixel+1) = 200;
- //*(pu8Pixel+2) = 200;
-
- bitmapData.setPixelColour(pInfo.samp,j,pInfo.lineColour);
-
- }
-}
-
-
-
-#pragma mark - LfpSupersampledBitmapPlotter -
-
-SupersampledBitmapPlotter::SupersampledBitmapPlotter(LfpDisplay * lfpDisplay)
- : LfpBitmapPlotter(lfpDisplay)
-{ }
-
-void SupersampledBitmapPlotter::plot(Image::BitmapData &bdLfpChannelBitmap, LfpBitmapPlotterInfo &pInfo)
-{
- std::array samplesThisPixel = pInfo.samplesPerPixel;
-// int sampleCountThisPixel = lfpDisplay->canvas->getSampleCountPerPixel(pInfo.samp);
- int sampleCountThisPixel = pInfo.sampleCountPerPixel;
-
- if (pInfo.samplerange>0 && sampleCountThisPixel>0)
- {
-
- //float localHist[samplerange]; // simple histogram
- Array rangeHist; // [samplerange]; // paired range histogram, same as plotting at higher res. and subsampling
-
- for (int k = 0; k <= pInfo.samplerange; k++)
- rangeHist.add(0);
-
- for (int k = 0; k <= sampleCountThisPixel; k++) // add up paired-range histogram per pixel - for each pair fill intermediate with uniform distr.
- {
- int cs_this = (((samplesThisPixel[k]/pInfo.range*pInfo.channelHeightFloat)+pInfo.height/2)-pInfo.from); // sample values -> pixel coordinates relative to from
- int cs_next = (((samplesThisPixel[k+1]/pInfo.range*pInfo.channelHeightFloat)+pInfo.height/2)-pInfo.from);
-
-
- if (cs_this<0) {cs_this=0;}; //here we could clip the diaplay to the max/min, or ignore out of bound values, not sure which one is better
- if (cs_this>pInfo.samplerange) {cs_this=pInfo.samplerange;};
- if (cs_next<0) {cs_next=0;};
- if (cs_next>pInfo.samplerange) {cs_next=pInfo.samplerange;};
-
- int hfrom=0;
- int hto=0;
-
- if (cs_this1.0f) {a=1.0f;};
- if (a<0.0f) {a=0.0f;};
-
-
- //Colour gradedColor = lineColour.withMultipliedBrightness(2.0f).interpolatedWith(lineColour.withMultipliedSaturation(0.6f).withMultipliedBrightness(0.3f),1-a) ;
- Colour gradedColor = pInfo.lineColourBright.interpolatedWith(pInfo.lineColourDark,1-a);
- //Colour gradedColor = Colour(0,255,0);
-
- int ploty = pInfo.from + s + pInfo.y;
- if(ploty>0 && ploty < display->lfpChannelBitmap.getHeight()) {
- bdLfpChannelBitmap.setPixelColour(pInfo.samp, pInfo.from + s + pInfo.y, gradedColor);
- }
- }
-
- } else {
-
- int ploty = pInfo.from + pInfo.y;
- if(ploty>0 && ploty < display->lfpChannelBitmap.getHeight()) {
- bdLfpChannelBitmap.setPixelColour(pInfo.samp, ploty, pInfo.lineColour);
- }
- }
-}
-
-
-
-#pragma mark - LfpChannelColourScheme -
-
-int LfpChannelColourScheme::colourGrouping = 1;
-
-void LfpChannelColourScheme::setColourGrouping(int grouping)
-{
- colourGrouping = grouping;
-}
-
-int LfpChannelColourScheme::getColourGrouping()
-{
- return colourGrouping;
-}
-
-
-
-#pragma mark - LfpDefaultColourScheme -
-
-Array LfpDefaultColourScheme::colourList = []() -> Array {
- Array colours;
- colours.add(Colour(224,185,36));
- colours.add(Colour(214,210,182));
- colours.add(Colour(243,119,33));
- colours.add(Colour(186,157,168));
- colours.add(Colour(237,37,36));
- colours.add(Colour(179,122,79));
- colours.add(Colour(217,46,171));
- colours.add(Colour(217, 139,196));
- colours.add(Colour(101,31,255));
- colours.add(Colour(141,111,181));
- colours.add(Colour(48,117,255));
- colours.add(Colour(184,198,224));
- colours.add(Colour(116,227,156));
- colours.add(Colour(150,158,155));
- colours.add(Colour(82,173,0));
- colours.add(Colour(125,99,32));
- return colours;
-}();
-
-LfpDefaultColourScheme::LfpDefaultColourScheme(LfpDisplay* display, LfpDisplayCanvas* canvas)
- : LfpViewer::LfpChannelColourScheme(LfpDefaultColourScheme::colourList.size(), display, canvas)
-{
- setName("Default");
-}
-
-void LfpDefaultColourScheme::paint(Graphics &g)
-{
-
-}
-
-void LfpDefaultColourScheme::resized()
-{
-
-}
-
-const Colour LfpDefaultColourScheme::getColourForIndex(int index) const
-{
-// return colourList[index % colourList.size()];
- return colourList[(int(index/colourGrouping)) % colourList.size()];
-}
-
-
-
-#pragma mark - LfpMonochromaticColorScheme
-
-LfpMonochromaticColourScheme::LfpMonochromaticColourScheme(LfpDisplay* display, LfpDisplayCanvas* canvas)
- : LfpChannelColourScheme (8, display, canvas)
- , isBlackAndWhite (false)
- , colourPattern (DOWN_UP)
-{
- setName("Monochromatic");
-
- numChannelsLabel = new Label("numChannelsLabel", "Num Color Steps");
- numChannelsLabel->setFont(Font("Default", 13.0f, Font::plain));
- numChannelsLabel->setColour(Label::textColourId, Colour(100, 100, 100));
- addAndMakeVisible(numChannelsLabel);
-
- StringArray numChannelsSelectionOptions = {"4", "8", "16"};
- numChannelsSelection = new ComboBox("numChannelsSelection");
- numChannelsSelection->addItemList(numChannelsSelectionOptions, 1);
- numChannelsSelection->setEditableText(true);
- numChannelsSelection->addListener(this);
- numChannelsSelection->setSelectedId(2, dontSendNotification);
- addAndMakeVisible(numChannelsSelection);
-
-
- baseHueLabel = new Label("baseHue", "Hue");
- baseHueLabel->setFont(Font("Default", 13.0f, Font::plain));
- baseHueLabel->setColour(Label::textColourId, Colour(100, 100, 100));
- addAndMakeVisible(baseHueLabel);
-
- baseHueSlider = new Slider;
- baseHueSlider->setRange(0, 1);
- baseHueSlider->setValue(0);
- baseHueSlider->setTextBoxStyle(Slider::NoTextBox, false, 0, 0);
- baseHueSlider->addListener(this);
- addAndMakeVisible(baseHueSlider);
-
- baseHueSlider->addMouseListener(this, true);
-
-
- colourPatternLabel = new Label("colourPatternLabel", "Pattern");
- colourPatternLabel->setFont(Font("Default", 13.0f, Font::plain));
- colourPatternLabel->setColour(Label::textColourId, Colour(100, 100, 100));
- addAndMakeVisible(colourPatternLabel);
-
- StringArray colourPatternSelectionOptions = {"Down", "Up", "Down-Up", "Up-Down"};
- colourPatternSelection = new ComboBox("colourPatternSelection");
- colourPatternSelection->addItemList(colourPatternSelectionOptions, 1);
- colourPatternSelection->setEditableText(false);
- colourPatternSelection->addListener(this);
- colourPatternSelection->setSelectedId(colourPattern + 1, dontSendNotification);
- addAndMakeVisible(colourPatternSelection);
-
- baseHue = Colour::fromHSV(0, 1, 1, 1);
- swatchHue = baseHue;
-
- calculateColourSeriesFromBaseHue();
-}
-
-void LfpMonochromaticColourScheme::paint(Graphics &g)
-{
- g.setColour(swatchHue);
- g.fillRect(colourSwatchRect);
-}
-
-void LfpMonochromaticColourScheme::resized()
-{
- numChannelsLabel->setBounds(0, 5, 120, 25);
- numChannelsSelection->setBounds(numChannelsLabel->getRight(),
- numChannelsLabel->getY(),
- 60,
- 25);
-
- baseHueLabel->setBounds(0, numChannelsLabel->getBottom(), 35, 25);
- baseHueSlider->setBounds(baseHueLabel->getRight(),
- baseHueLabel->getY(),
- numChannelsSelection->getRight() - baseHueLabel->getRight() - 20,
- 25);
-
- colourSwatchRect.setBounds(baseHueSlider->getRight() + 5, baseHueSlider->getY() + 5, 15, baseHueSlider->getHeight() - 10);
-
- colourPatternLabel->setBounds(0, baseHueLabel->getBottom(), 80, 25);
- colourPatternSelection->setBounds(colourPatternLabel->getRight(),
- colourPatternLabel->getY(),
- numChannelsSelection->getRight() - colourPatternLabel->getRight(),
- 25);
-
-}
-
-void LfpMonochromaticColourScheme::sliderValueChanged(Slider *sl)
-{
- swatchHue = Colour::fromHSV(sl->getValue(), 1, 1, 1);
- repaint(colourSwatchRect);
-}
-
-void LfpMonochromaticColourScheme::comboBoxChanged(ComboBox *cb)
-{
- if (cb == numChannelsSelection)
- {
- int numChannelsColourSpread = 0;
- if (cb->getSelectedId())
- {
- numChannelsColourSpread = cb->getText().getIntValue();
- }
- else
- {
- numChannelsColourSpread = cb->getText().getIntValue();
- if (numChannelsColourSpread < 1) numChannelsColourSpread = 1;
- else if (numChannelsColourSpread > 16) numChannelsColourSpread = 16;
-
- cb->setText(String(numChannelsColourSpread), dontSendNotification);
- }
-
- setNumColourSeriesSteps(numChannelsColourSpread);
- }
- else if (cb == colourPatternSelection)
- {
- setColourPattern((ColourPattern)(cb->getSelectedId() - 1));
- }
- calculateColourSeriesFromBaseHue();
- lfpDisplay->setColors();
-// canvas->fullredraw = true;
- canvas->redraw();
-}
-
-void LfpMonochromaticColourScheme::mouseUp(const MouseEvent &e)
-{
- if (swatchHue.getARGB() != baseHue.getARGB())
- {
- baseHue = swatchHue;
- calculateColourSeriesFromBaseHue();
- lfpDisplay->setColors();
- canvas->redraw();
- }
-}
-
-void LfpMonochromaticColourScheme::setBaseHue(Colour base)
-{
- baseHue = base;
- calculateColourSeriesFromBaseHue();
-}
-
-const Colour LfpMonochromaticColourScheme::getBaseHue() const
-{
- return baseHue;
-}
-
-void LfpMonochromaticColourScheme::setNumColourSeriesSteps(int numSteps)
-{
- numColourChannels = numSteps;
-}
-
-int LfpMonochromaticColourScheme::getNumColourSeriesSteps()
-{
- return numColourChannels;
-}
-
-const Colour LfpMonochromaticColourScheme::getColourForIndex(int index) const
-{
- int colourIdx = (int(index/colourGrouping) % numColourChannels);
-
- // adjust for oscillating patterns
- if (colourPattern == DOWN_UP || colourPattern == UP_DOWN)
- {
- int mid = numColourChannels / 2;
- if (colourIdx > mid)
- {
- if (numColourChannels % 2 == 0)
- colourIdx = numColourChannels - colourIdx;
- else
- colourIdx = (numColourChannels - colourIdx) * 2 - 1;
- }
- else if (numColourChannels % 2 != 0)
- {
- colourIdx *= 2;
- }
- }
-
- // invert if the pattern is UP or UP_DOWN
- if (colourPattern == UP)
- colourIdx = (numColourChannels - 1) - colourIdx;
- else if (colourPattern == UP_DOWN)
- colourIdx = (colourList.size() - 1) - colourIdx;
-
- return colourList[colourIdx];
-}
-
-void LfpMonochromaticColourScheme::calculateColourSeriesFromBaseHue()
-{
- colourList.clear();
-
- int coloursToCalculate = numColourChannels;
-
- if (numColourChannels % 2 == 0 && (colourPattern == DOWN_UP || colourPattern == UP_DOWN))
- {
- coloursToCalculate = coloursToCalculate / 2 + 1;
- }
-
- for (int i = 0; i < coloursToCalculate; ++i)
- {
- float saturation = 1 - (i / float(coloursToCalculate + 1));
- colourList.add(baseHue.withMultipliedSaturation(saturation));
- }
-}
-
-
-
-#pragma mark - LfpGradientColourScheme
-
-LfpGradientColourScheme::LfpGradientColourScheme(LfpDisplay * display, LfpDisplayCanvas * canvas)
- : LfpMonochromaticColourScheme(display, canvas)
-{
- setName("Gradient");
-
- baseHueLabel->setName("baseHueA");
- baseHueLabel->setText("Hue A", dontSendNotification);
-
- baseHueLabelB = new Label("baseHueB", "Hue B");
- baseHueLabelB->setFont(Font("Default", 13.0f, Font::plain));
- baseHueLabelB->setColour(Label::textColourId, Colour(100, 100, 100));
- addAndMakeVisible(baseHueLabelB);
-
- baseHueSliderB = new Slider;
- baseHueSliderB->setRange(0, 1);
- baseHueSliderB->setValue(0.5);
- baseHueSliderB->setTextBoxStyle(Slider::NoTextBox, false, 0, 0);
- baseHueSliderB->addListener(this);
- addAndMakeVisible(baseHueSliderB);
-
- baseHueSliderB->addMouseListener(this, true);
-
- baseHueB = Colour::fromHSV(0.5, 1.0, 1.0, 1.0);
- swatchHueB = baseHueB;
-
- calculateColourSeriesFromBaseHue();
-}
-
-void LfpGradientColourScheme::paint(Graphics &g)
-{
- g.setColour(swatchHue);
- g.fillRect(colourSwatchRect);
-
- g.setColour(swatchHueB);
- g.fillRect(colourSwatchRectB);
-}
-
-void LfpGradientColourScheme::resized()
-{
- numChannelsLabel->setBounds(0, 5, 120, 25);
- numChannelsSelection->setBounds(numChannelsLabel->getRight(),
- numChannelsLabel->getY(),
- 60,
- 25);
-
- baseHueLabel->setBounds(0, numChannelsLabel->getBottom(), 35, 25);
- baseHueSlider->setBounds(baseHueLabel->getRight(),
- baseHueLabel->getY(),
- numChannelsSelection->getRight() - baseHueLabel->getRight() - 20,
- 25);
-
- colourSwatchRect.setBounds(baseHueSlider->getRight() + 5, baseHueSlider->getY() + 5, 15, baseHueSlider->getHeight() - 10);
-
- baseHueLabelB->setBounds(0, baseHueLabel->getBottom(), 35, 25);
- baseHueSliderB->setBounds(baseHueLabelB->getRight(),
- baseHueLabelB->getY(),
- numChannelsSelection->getRight() - baseHueLabelB->getRight() - 20,
- 25);
-
- colourSwatchRectB.setBounds(baseHueSliderB->getRight() + 5, baseHueSliderB->getY() + 5, 15, baseHueSliderB->getHeight() - 10);
-
- colourPatternLabel->setBounds(0, baseHueLabelB->getBottom(), 80, 25);
- colourPatternSelection->setBounds(colourPatternLabel->getRight(),
- colourPatternLabel->getY(),
- numChannelsSelection->getRight() - colourPatternLabel->getRight(),
- 25);
-}
-
-void LfpGradientColourScheme::sliderValueChanged(Slider *sl)
-{
- if (sl == baseHueSlider)
- {
- swatchHue = Colour::fromHSV(sl->getValue(), 1, 1, 1);
- repaint(colourSwatchRect);
- }
- else
- {
- swatchHueB = Colour::fromHSV(sl->getValue(), 1, 1, 1);
- repaint(colourSwatchRectB);
- }
-}
-
-void LfpGradientColourScheme::mouseUp(const MouseEvent &e)
-{
- if (e.originalComponent == baseHueSlider)
- {
- if (swatchHue.getARGB() != baseHue.getARGB())
- {
- baseHue = swatchHue;
- calculateColourSeriesFromBaseHue();
- lfpDisplay->setColors();
- canvas->redraw();
- }
- }
- else
- {
- if (swatchHueB.getARGB() != baseHueB.getARGB())
- {
- baseHueB = swatchHueB;
- calculateColourSeriesFromBaseHue();
- lfpDisplay->setColors();
- canvas->redraw();
- }
- }
-}
-
-void LfpGradientColourScheme::calculateColourSeriesFromBaseHue()
-{
- colourList.clear();
-
- int coloursToCalculate = numColourChannels;
-
- if (numColourChannels % 2 == 0 && (colourPattern == DOWN_UP || colourPattern == UP_DOWN))
- {
- coloursToCalculate = coloursToCalculate / 2 + 1;
- }
-
- for (int i = 0; i < coloursToCalculate; ++i)
- {
- float hue = (baseHueB.getHue() - baseHue.getHue()) * i / float(coloursToCalculate - 1);
- colourList.add(baseHue.withRotatedHue(hue));
- }
-}
diff --git a/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp.orig b/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp.orig
new file mode 100644
index 0000000000..5298244e46
--- /dev/null
+++ b/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp.orig
@@ -0,0 +1,4482 @@
+/*
+------------------------------------------------------------------
+
+This file is part of the Open Ephys GUI
+Copyright (C) 2013 Open Ephys
+
+------------------------------------------------------------------
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "LfpDisplayCanvas.h"
+
+#include
+
+using namespace LfpViewer;
+
+
+
+#pragma mark - LfpDisplayCanvas -
+
+LfpDisplayCanvas::LfpDisplayCanvas(LfpDisplayNode* processor_) :
+ timebase(1.0f), displayGain(1.0f), timeOffset(0.0f),
+ processor(processor_)
+{
+
+ nChans = processor->getNumSubprocessorChannels();
+
+ displayBuffer = processor->getDisplayBufferAddress();
+ displayBufferSize = displayBuffer->getNumSamples();
+
+ screenBuffer = new AudioSampleBuffer(MAX_N_CHAN, MAX_N_SAMP);
+ screenBuffer->clear();
+
+ screenBufferMin = new AudioSampleBuffer(MAX_N_CHAN, MAX_N_SAMP);
+ screenBufferMin->clear();
+ screenBufferMean = new AudioSampleBuffer(MAX_N_CHAN, MAX_N_SAMP);
+ screenBufferMean->clear();
+ screenBufferMax = new AudioSampleBuffer(MAX_N_CHAN, MAX_N_SAMP);
+ screenBufferMax->clear();
+
+ viewport = new LfpViewport(this);
+ lfpDisplay = new LfpDisplay(this, viewport);
+ timescale = new LfpTimescale(this, lfpDisplay);
+ options = new LfpDisplayOptions(this, timescale, lfpDisplay, processor);
+
+ lfpDisplay->options = options;
+
+ timescale->setTimebase(timebase);
+
+ viewport->setViewedComponent(lfpDisplay, false);
+ viewport->setScrollBarsShown(true, false);
+
+ scrollBarThickness = viewport->getScrollBarThickness();
+
+ isChannelEnabled.insertMultiple(0,true,10000); // max 10k channels
+
+ //viewport->getVerticalScrollBar()->addListener(this->scrollBarMoved(viewport->getVerticalScrollBar(), 1.0));
+
+ addAndMakeVisible(viewport);
+ addAndMakeVisible(timescale);
+ addAndMakeVisible(options);
+
+ lfpDisplay->setNumChannels(nChans);
+
+ resizeSamplesPerPixelBuffer(nChans);
+
+ TopLevelWindow::getTopLevelWindow(0)->addKeyListener(this);
+
+ optionsDrawerIsOpen = false;
+
+}
+
+LfpDisplayCanvas::~LfpDisplayCanvas()
+{
+
+ // de-allocate 3d-array samplesPerPixel [nChans][MAX_N_SAMP][MAX_N_SAMP_PER_PIXEL];
+
+ //for(int i=0;iremoveKeyListener(this);
+}
+
+void LfpDisplayCanvas::resizeSamplesPerPixelBuffer(int numCh)
+{
+ // allocate samplesPerPixel, behaves like float samplesPerPixel[nChans][MAX_N_SAMP][MAX_N_SAMP_PER_PIXEL]
+ //samplesPerPixel = (float***)malloc(nChans * sizeof(float **));
+
+ // 3D array: dimensions channels x samples x samples per pixel
+ samplesPerPixel.clear();
+ samplesPerPixel.resize(numCh);
+
+ //for(int i = 0; i < numCh; i++)
+ //{
+ //std::vector< std::vector> v1;
+ // samplesPerPixel[i].resize(MAX_N_SAMP);
+ //samplesPerPixel.push_back(v1);
+ //samplesPerPixel[i] = (float**)malloc(MAX_N_SAMP * sizeof(float*));
+
+ // for(int j = 0; j < MAX_N_SAMP; j++)
+ // {
+ //std::vector v2;
+ //v2.resize(MAX_N_SAMP_PER_PIXEL);
+ // samplesPerPixel[i][j].resize(MAX_N_SAMP_PER_PIXEL);
+ // //samplesPerPixel[i][j] = (float*)malloc(MAX_N_SAMP_PER_PIXEL*sizeof(float));
+ // }
+ //}
+}
+
+void LfpDisplayCanvas::toggleOptionsDrawer(bool isOpen)
+{
+ optionsDrawerIsOpen = isOpen;
+ auto viewportPosition = viewport->getViewPositionY(); // remember viewport position
+ resized();
+ viewport->setViewPosition(0, viewportPosition); // return viewport position
+}
+
+void LfpDisplayCanvas::resized()
+{
+
+ timescale->setBounds(leftmargin,0,getWidth()-scrollBarThickness-leftmargin,30);
+ viewport->setBounds(0,30,getWidth(),getHeight()-90);
+
+ if (nChans > 0)
+ {
+ if (lfpDisplay->getSingleChannelState())
+ lfpDisplay->setChannelHeight(viewport->getHeight(),false);
+
+ lfpDisplay->setBounds(0,0,getWidth()-scrollBarThickness, lfpDisplay->getChannelHeight()*lfpDisplay->drawableChannels.size());
+ }
+ else
+ {
+ lfpDisplay->setBounds(0, 0, getWidth(), getHeight());
+ }
+
+ if (optionsDrawerIsOpen)
+ options->setBounds(0, getHeight()-200, getWidth(), 200);
+ else
+ options->setBounds(0, getHeight()-55, getWidth(), 55);
+
+}
+
+void LfpDisplayCanvas::resizeToChannels(bool respectViewportPosition)
+{
+ lfpDisplay->setBounds(0,0,getWidth()-scrollBarThickness, lfpDisplay->getChannelHeight()*lfpDisplay->drawableChannels.size());
+
+ // if param is flagged, move the viewport scroll back to same relative position before
+ // resize took place
+ if (!respectViewportPosition) return;
+
+ // get viewport scroll position as ratio against lfpDisplay's dims
+ // so that we can set scrollbar back to rough position before resize
+ // (else viewport scrolls back to top after resize)
+ const double yPositionRatio = viewport->getViewPositionY() / (double)lfpDisplay->getHeight();
+ const double xPositionRatio = viewport->getViewPositionX() / (double)lfpDisplay->getWidth();
+
+ viewport->setViewPosition(lfpDisplay->getWidth() * xPositionRatio,
+ lfpDisplay->getHeight() * yPositionRatio);
+}
+
+void LfpDisplayCanvas::beginAnimation()
+{
+
+ if (true)
+ {
+
+ displayBufferSize = displayBuffer->getNumSamples();
+
+ for (int i = 0; i < screenBufferIndex.size(); i++)
+ {
+ screenBufferIndex.set(i, 0);
+ }
+
+ startCallbacks();
+ }
+}
+
+void LfpDisplayCanvas::endAnimation()
+{
+ if (true)
+ {
+
+ stopCallbacks();
+ }
+}
+
+void LfpDisplayCanvas::update()
+{
+
+ displayBufferSize = displayBuffer->getNumSamples();
+
+ nChans = jmax(processor->getNumSubprocessorChannels(), 0);
+
+ resizeSamplesPerPixelBuffer(nChans);
+
+ sampleRate = 30000; // default
+
+ for (auto* arr : { &screenBufferIndex, &lastScreenBufferIndex, &displayBufferIndex })
+ {
+ arr->clearQuick();
+ arr->insertMultiple(0, 0, nChans + 1); // extra channel for events
+ }
+
+ options->setEnabled(nChans != 0);
+ // must manually ensure that overlapSelection propagates up to canvas
+ channelOverlapFactor = options->selectedOverlapValue.getFloatValue();
+
+ int firstChannelInSubprocessor = 0;
+ for (int i = 0, nInputs = processor->getNumInputs(); i < nInputs; i++)
+ {
+
+ if (processor->getDataSubprocId(i) == drawableSubprocessor)
+ {
+ sampleRate = processor->getDataChannel(i)->getSampleRate();
+ firstChannelInSubprocessor = i;
+ break;
+ }
+
+ }
+
+ if (nChans != lfpDisplay->getNumChannels())
+ {
+ refreshScreenBuffer();
+
+ if (nChans > 0)
+ lfpDisplay->setNumChannels(nChans);
+
+ // update channel names
+ //std::cout << "Updating channel names" << std::endl;
+ for (int i = 0; i < nChans; i++)
+ {
+ String chName = processor->getDataChannel(firstChannelInSubprocessor + i)->getName();
+ lfpDisplay->channelInfo[i]->setName(chName);
+ lfpDisplay->setEnabledState(isChannelEnabled[i], i);
+ }
+
+ if (nChans == 0) lfpDisplay->setBounds(0, 0, getWidth(), getHeight());
+ else {
+ lfpDisplay->rebuildDrawableChannelsList();
+ lfpDisplay->setBounds(0, 0, getWidth()-scrollBarThickness*2, lfpDisplay->getTotalHeight());
+ }
+
+ resized();
+ }
+ else
+ {
+ for (int i = 0; i < nChans; i++)
+ {
+ String chName = processor->getDataChannel(firstChannelInSubprocessor + i)->getName();
+ lfpDisplay->channelInfo[i]->setName(chName);
+ lfpDisplay->channels[i]->updateType();
+ lfpDisplay->channelInfo[i]->updateType();
+ }
+
+ if (nChans > 0)
+ {
+ lfpDisplay->rebuildDrawableChannelsList();
+ }
+ }
+}
+
+
+
+int LfpDisplayCanvas::getChannelHeight()
+{
+ //return spreads[spreadSelection->getSelectedId()-1].getIntValue();
+ return options->getChannelHeight();
+
+}
+
+
+void LfpDisplayCanvas::setParameter(int param, float val)
+{
+ // not used for anything, since LfpDisplayCanvas is not a processor
+}
+
+
+void LfpDisplayCanvas::refreshState()
+{
+ // called when the component's tab becomes visible again
+
+ if (true)
+ {
+ for (int i = 0; i <= displayBufferIndex.size(); i++) // include event channel
+ {
+
+ displayBufferIndex.set(i, processor->getDisplayBufferIndex(i));
+ screenBufferIndex.set(i, 0);
+ }
+ }
+
+}
+
+void LfpDisplayCanvas::refreshScreenBuffer()
+{
+ if (true)
+ {
+ for (int i = 0; i < screenBufferIndex.size(); i++)
+ screenBufferIndex.set(i, 0);
+
+ screenBuffer->clear();
+ screenBufferMin->clear();
+ screenBufferMean->clear();
+ screenBufferMax->clear();
+ }
+
+}
+
+void LfpDisplayCanvas::updateScreenBuffer()
+{
+ if (true)
+ {
+ // copy new samples from the displayBuffer into the screenBuffer
+ int maxSamples = lfpDisplay->getWidth() - leftmargin;
+
+ ScopedLock displayLock(*processor->getMutex());
+
+ for (int channel = 0; channel <= nChans; channel++) // pull one extra channel for event display
+ {
+
+ //if (channel == 0)
+ // std::cout << sampleRate[channel] << std::endl;
+
+ if (screenBufferIndex[channel] >= maxSamples) // wrap around if we reached right edge before
+ screenBufferIndex.set(channel, 0);
+
+ // hold these values locally for each channel - is this a good idea?
+ int sbi = screenBufferIndex[channel];
+ int dbi = displayBufferIndex[channel];
+
+
+
+ lastScreenBufferIndex.set(channel, sbi);
+
+ int index = processor->getDisplayBufferIndex(channel);
+
+ int nSamples = index - dbi; // N new samples (not pixels) to be added to displayBufferIndex
+
+ if (nSamples < 0) // buffer has reset to 0 -- xxx 2do bug: this shouldnt happen because it makes the range/histogram display not work properly/look off for one pixel
+ {
+ nSamples = (displayBufferSize - dbi) + index + 1;
+ // std::cout << "nsamples 0 " ;
+ }
+
+ //if (channel == 15 || channel == 16)
+ // std::cout << channel << " " << sbi << " " << dbi << " " << nSamples << std::endl;
+
+
+ float ratio = sampleRate * timebase / float(getWidth() - leftmargin - scrollBarThickness); // samples / pixel
+ // this number is crucial: converting from samples to values (in px) for the screen buffer
+ int valuesNeeded = (int) float(nSamples) / ratio; // N pixels needed for this update
+
+ if (sbi + valuesNeeded > maxSamples) // crop number of samples to fit canvas width
+ {
+ valuesNeeded = maxSamples - sbi;
+ }
+ float subSampleOffset = 0.0;
+
+ dbi %= displayBufferSize; // make sure we're not overshooting
+ int nextPos = (dbi + 1) % displayBufferSize; // position next to displayBufferIndex in display buffer to copy from
+
+ // if (channel == 0)
+ // std::cout << "Channel "
+ // << channel << " : "
+ // << sbi << " : "
+ // << index << " : "
+ // << dbi << " : "
+ // << valuesNeeded << " : "
+ // << ratio
+ // << std::endl;
+
+ if (valuesNeeded > 0 && valuesNeeded < 1000000)
+ {
+ for (int i = 0; i < valuesNeeded; i++) // also fill one extra sample for line drawing interpolation to match across draws
+ {
+ //If paused don't update screen buffers, but update all indexes as needed
+ if (!lfpDisplay->isPaused)
+ {
+ float gain = 1.0;
+ float alpha = (float)subSampleOffset;
+ float invAlpha = 1.0f - alpha;
+
+ screenBuffer->clear(channel, sbi, 1);
+ screenBufferMean->clear(channel, sbi, 1);
+ screenBufferMin->clear(channel, sbi, 1);
+ screenBufferMax->clear(channel, sbi, 1);
+
+ dbi %= displayBufferSize; // just to be sure
+
+ // update continuous data channels
+ if (channel != nChans)
+ {
+ // interpolate between two samples with invAlpha and alpha
+ screenBuffer->addFrom(channel, // destChannel
+ sbi, // destStartSample
+ displayBuffer->getReadPointer(channel, dbi), // source
+ 1, // numSamples
+ invAlpha*gain); // gain
+
+
+ screenBuffer->addFrom(channel, // destChannel
+ sbi, // destStartSample
+ displayBuffer->getReadPointer(channel, nextPos), // source
+ 1, // numSamples
+ alpha*gain); // gain
+ }
+
+
+ // same thing again, but this time add the min,mean, and max of all samples in current pixel
+ float sample_min = 10000000;
+ float sample_max = -10000000;
+ float sample_mean = 0;
+
+ int nextpix = (dbi + (int)ratio + 1) % (displayBufferSize + 1); // position to next pixels index
+
+ if (nextpix <= dbi) { // at the end of the displaybuffer, this can occur and it causes the display to miss one pixel woth of sample - this circumvents that
+ // std::cout << "np " ;
+ nextpix = dbi;
+ }
+
+ for (int j = dbi; j < nextpix; j++)
+ {
+
+ float sample_current = displayBuffer->getSample(channel, j);
+ sample_mean = sample_mean + sample_current;
+
+ if (sample_min > sample_current)
+ {
+ sample_min = sample_current;
+ }
+
+ if (sample_max < sample_current)
+ {
+ sample_max = sample_current;
+ }
+
+ }
+
+ // update event channel
+ if (channel == nChans)
+ {
+ //std::cout << sample_max << std::endl;
+ screenBuffer->setSample(channel, sbi, sample_max);
+ //if (screenBuffer->getSample(channel, sbi - 1) != sample_max)
+ // std::cout << "Sample changed" << std::endl;
+ //screenBuffer->setSample(channel, sbi, sample_max);
+ }
+
+ // similarly, for each pixel on the screen, we want a list of all values so we can draw a histogram later
+ // for simplicity, we'll just do this as 2d array, samplesPerPixel[px][samples]
+ // with an additional array sampleCountPerPixel[px] that holds the N samples per pixel
+ if (channel < nChans) // we're looping over one 'extra' channel for events above, so make sure not to loop over that one here
+ {
+ int c = 0;
+ for (int j = dbi; j < nextpix && c < MAX_N_SAMP_PER_PIXEL; j++)
+ {
+ float sample_current = displayBuffer->getSample(channel, j);
+ samplesPerPixel[channel][sbi][c] = sample_current;
+ c++;
+ }
+ if (c > 0){
+ sampleCountPerPixel[sbi] = c - 1; // save count of samples for this pixel
+ }
+ else{
+ sampleCountPerPixel[sbi] = 0;
+ }
+ sample_mean = sample_mean / c;
+ screenBufferMean->addSample(channel, sbi, sample_mean*gain);
+
+ screenBufferMin->addSample(channel, sbi, sample_min*gain);
+ screenBufferMax->addSample(channel, sbi, sample_max*gain);
+ }
+ sbi++;
+ }
+
+ subSampleOffset += ratio;
+
+ while (subSampleOffset >= 1.0)
+ {
+ if (++dbi > displayBufferSize)
+ dbi = 0;
+
+ nextPos = (dbi + 1) % displayBufferSize;
+ subSampleOffset -= 1.0;
+ }
+
+ }
+
+ // update values after we're done
+ screenBufferIndex.set(channel, sbi);
+ displayBufferIndex.set(channel, dbi);
+ }
+
+ }
+ }
+
+}
+
+const float LfpDisplayCanvas::getXCoord(int chan, int samp)
+{
+ return samp;
+}
+
+int LfpDisplayCanvas::getNumChannels()
+{
+ return nChans;
+}
+
+int LfpDisplayCanvas::getNumChannelsVisible()
+{
+ return lfpDisplay->drawableChannels.size();
+}
+
+int LfpDisplayCanvas::getChannelSubprocessorIdx(int channel)
+{
+ return processor->getDataChannel(channel)->getSubProcessorIdx();
+}
+
+const float LfpDisplayCanvas::getYCoord(int chan, int samp)
+{
+ return *screenBuffer->getReadPointer(chan, samp);
+}
+
+const float LfpDisplayCanvas::getYCoordMean(int chan, int samp)
+{
+ return *screenBufferMean->getReadPointer(chan, samp);
+}
+const float LfpDisplayCanvas::getYCoordMin(int chan, int samp)
+{
+ return *screenBufferMin->getReadPointer(chan, samp);
+}
+const float LfpDisplayCanvas::getYCoordMax(int chan, int samp)
+{
+ return *screenBufferMax->getReadPointer(chan, samp);
+}
+
+std::array LfpDisplayCanvas::getSamplesPerPixel(int chan, int px)
+{
+ return samplesPerPixel[chan][px];
+}
+const int LfpDisplayCanvas::getSampleCountPerPixel(int px)
+{
+ return sampleCountPerPixel[px];
+}
+
+float LfpDisplayCanvas::getMean(int chan)
+{
+ float total = 0.0f;
+ float numPts = 0;
+
+ float sample = 0.0f;
+ for (int samp = 0; samp < (lfpDisplay->getWidth() - leftmargin); samp += 10)
+ {
+ sample = *screenBuffer->getReadPointer(chan, samp);
+ total += sample;
+ numPts++;
+ }
+
+ //std::cout << sample << std::endl;
+
+ return total / numPts;
+}
+
+float LfpDisplayCanvas::getStd(int chan)
+{
+ float std = 0.0f;
+
+ float mean = getMean(chan);
+ float numPts = 1;
+
+ for (int samp = 0; samp < (lfpDisplay->getWidth() - leftmargin); samp += 10)
+ {
+ std += pow((*screenBuffer->getReadPointer(chan, samp) - mean),2);
+ numPts++;
+ }
+
+ return sqrt(std / numPts);
+
+}
+
+bool LfpDisplayCanvas::getInputInvertedState()
+{
+ return options->getInputInvertedState(); //invertInputButton->getToggleState();
+}
+
+bool LfpDisplayCanvas::getDisplaySpikeRasterizerState()
+{
+ return options->getDisplaySpikeRasterizerState();
+}
+
+bool LfpDisplayCanvas::getDrawMethodState()
+{
+
+ return options->getDrawMethodState(); //drawMethodButton->getToggleState();
+}
+
+void LfpDisplayCanvas::setDrawableSampleRate(float samplerate)
+{
+// std::cout << "setting the drawable sample rate in the canvas" << std::endl;
+ displayedSampleRate = samplerate;
+}
+
+void LfpDisplayCanvas::setDrawableSubprocessor(uint32 sp)
+{
+ drawableSubprocessor = sp;
+ displayBuffer = processor->getDisplayBufferAddress();
+ update();
+}
+
+void LfpDisplayCanvas::redraw()
+{
+ fullredraw=true;
+ repaint();
+ refresh();
+}
+
+
+void LfpDisplayCanvas::paint(Graphics& g)
+{
+
+ //std::cout << "Painting" << std::endl;
+
+ //g.setColour(Colour(0,0,0)); // for high-precision per-pixel density display, make background black for better visibility
+ g.setColour(lfpDisplay->backgroundColour); //background color
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ g.setGradientFill(ColourGradient(Colour(50,50,50),0,0,
+ Colour(25,25,25),0,30,
+ false));
+
+ g.fillRect(0, 0, getWidth()-scrollBarThickness, 30);
+
+ g.setColour(Colours::black);
+
+ g.drawLine(0,30,getWidth()-scrollBarThickness,30);
+
+ g.setColour(Colour(25,25,60)); // timing grid color
+
+ int w = getWidth()-scrollBarThickness-leftmargin;
+
+ for (int i = 0; i < 10; i++)
+ {
+ if (i == 5 || i == 0)
+ g.drawLine(w/10*i+leftmargin,timescale->getHeight(),w/10*i+leftmargin,getHeight()-60-timescale->getHeight(),3.0f);
+ else
+ g.drawLine(w/10*i+leftmargin,timescale->getHeight(),w/10*i+leftmargin,getHeight()-60-timescale->getHeight(),1.0f);
+ }
+
+ g.drawLine(0,getHeight()-60,getWidth(),getHeight()-60,3.0f);
+
+
+}
+
+void LfpDisplayCanvas::refresh()
+{
+ if (true)
+ {
+ updateScreenBuffer();
+
+ lfpDisplay->refresh(); // redraws only the new part of the screen buffer
+ }
+}
+
+bool LfpDisplayCanvas::keyPressed(const KeyPress& key)
+{
+ if (key.getKeyCode() == key.spaceKey)
+ {
+ options->togglePauseButton();
+
+ return true;
+ }
+
+ return false;
+}
+
+bool LfpDisplayCanvas::keyPressed(const KeyPress& key, Component* orig)
+{
+ if (getTopLevelComponent() == orig && isVisible())
+ {
+ return keyPressed(key);
+ }
+ return false;
+}
+
+void LfpDisplayCanvas::saveVisualizerParameters(XmlElement* xml)
+{
+
+ options->saveParameters(xml);
+ LfpDisplayEditor* ed = (LfpDisplayEditor*) processor->getEditor();
+ ed->saveVisualizerParameters(xml);
+}
+
+
+void LfpDisplayCanvas::loadVisualizerParameters(XmlElement* xml)
+{
+ options->loadParameters(xml);
+ LfpDisplayEditor* ed = (LfpDisplayEditor*) processor->getEditor();
+ ed->loadVisualizerParameters(xml);
+}
+
+
+
+#pragma mark - ShowHideOptionsButton -
+// =============================================================
+
+
+ShowHideOptionsButton::ShowHideOptionsButton(LfpDisplayOptions* options) : Button("Button")
+{
+ setClickingTogglesState(true);
+}
+ShowHideOptionsButton::~ShowHideOptionsButton()
+{
+
+}
+
+void ShowHideOptionsButton::paintButton(Graphics& g, bool, bool)
+{
+ g.setColour(Colours::white);
+
+ Path p;
+
+ float h = getHeight();
+ float w = getWidth();
+
+ if (getToggleState())
+ {
+ p.addTriangle(0.5f*w, 0.2f*h,
+ 0.2f*w, 0.8f*h,
+ 0.8f*w, 0.8f*h);
+ }
+ else
+ {
+ p.addTriangle(0.8f*w, 0.8f*h,
+ 0.2f*w, 0.5f*h,
+ 0.8f*w, 0.2f*h);
+ }
+
+ PathStrokeType pst = PathStrokeType(1.0f, PathStrokeType::curved, PathStrokeType::rounded);
+
+ g.strokePath(p, pst);
+}
+
+
+
+#pragma mark - LfpDisplayOptions -
+// -------------------------------------------------------------
+
+LfpDisplayOptions::LfpDisplayOptions(LfpDisplayCanvas* canvas_, LfpTimescale* timescale_,
+ LfpDisplay* lfpDisplay_, LfpDisplayNode* processor_)
+ : canvas(canvas_),
+ lfpDisplay(lfpDisplay_),
+ timescale(timescale_),
+ processor(processor_),
+ selectedChannelType(DataChannel::HEADSTAGE_CHANNEL),
+ labelFont("Default", 13.0f, Font::plain),
+ labelColour(100, 100, 100)
+{
+ // draw the colour scheme options
+ // TODO: (kelly) this might be better as a modal window
+ colourSchemeOptionLabel = new Label("colorSchemeOptionLabel", "Color Scheme");
+ colourSchemeOptionLabel->setFont(labelFont);
+ colourSchemeOptionLabel->setColour(Label::textColourId, labelColour);
+ addAndMakeVisible(colourSchemeOptionLabel);
+
+ StringArray colourSchemeNames = lfpDisplay->getColourSchemeNameArray();
+ colourSchemeOptionSelection = new ComboBox("colorSchemeOptionSelection");
+ colourSchemeOptionSelection->addItemList(colourSchemeNames, 1);
+ colourSchemeOptionSelection->setEditableText(false);
+ colourSchemeOptionSelection->addListener(this);
+ colourSchemeOptionSelection->setSelectedId(1, dontSendNotification);
+ addAndMakeVisible(colourSchemeOptionSelection);
+
+ if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
+ addAndMakeVisible(lfpDisplay->getColourSchemePtr());
+
+ //Ranges for neural data
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("25");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("50");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("100");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("250");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("400");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("500");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("750");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("1000");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("2000");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("5000");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("10000");
+ voltageRanges[DataChannel::HEADSTAGE_CHANNEL].add("15000");
+ selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL] = 4;
+ rangeGain[DataChannel::HEADSTAGE_CHANNEL] = 1; //uV
+ rangeSteps[DataChannel::HEADSTAGE_CHANNEL] = 10;
+ rangeUnits.add(CharPointer_UTF8("\xC2\xB5V"));
+ typeNames.add("DATA");
+
+ UtilityButton* tbut;
+ tbut = new UtilityButton("DATA",Font("Small Text", 9, Font::plain));
+ tbut->setEnabledState(true);
+ tbut->setCorners(false,false,false,false);
+ tbut->addListener(this);
+ tbut->setClickingTogglesState(true);
+ tbut->setRadioGroupId(100,dontSendNotification);
+ tbut->setToggleState(true,dontSendNotification);
+ addAndMakeVisible(tbut);
+ typeButtons.add(tbut);
+
+ //Ranges for AUX/accelerometer data
+ voltageRanges[DataChannel::AUX_CHANNEL].add("25");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("50");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("100");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("250");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("400");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("500");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("750");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("1000");
+ voltageRanges[DataChannel::AUX_CHANNEL].add("2000");
+ //voltageRanges[DataChannel::AUX_CHANNEL].add("5000");
+ selectedVoltageRange[DataChannel::AUX_CHANNEL] = 9;
+ rangeGain[DataChannel::AUX_CHANNEL] = 0.001f; //mV
+ rangeSteps[DataChannel::AUX_CHANNEL] = 10;
+ rangeUnits.add("mV");
+ typeNames.add("AUX");
+
+ tbut = new UtilityButton("AUX",Font("Small Text", 9, Font::plain));
+ tbut->setEnabledState(true);
+ tbut->setCorners(false,false,false,false);
+ tbut->addListener(this);
+ tbut->setClickingTogglesState(true);
+ tbut->setRadioGroupId(100,dontSendNotification);
+ tbut->setToggleState(false,dontSendNotification);
+ addAndMakeVisible(tbut);
+ typeButtons.add(tbut);
+
+ //Ranges for ADC data
+ voltageRanges[DataChannel::ADC_CHANNEL].add("0.01");
+ voltageRanges[DataChannel::ADC_CHANNEL].add("0.05");
+ voltageRanges[DataChannel::ADC_CHANNEL].add("0.1");
+ voltageRanges[DataChannel::ADC_CHANNEL].add("0.5");
+ voltageRanges[DataChannel::ADC_CHANNEL].add("1.0");
+ voltageRanges[DataChannel::ADC_CHANNEL].add("2.0");
+ voltageRanges[DataChannel::ADC_CHANNEL].add("5.0");
+ voltageRanges[DataChannel::ADC_CHANNEL].add("10.0");
+ selectedVoltageRange[DataChannel::ADC_CHANNEL] = 8;
+ rangeGain[DataChannel::ADC_CHANNEL] = 1; //V
+ rangeSteps[DataChannel::ADC_CHANNEL] = 0.1; //in V
+ rangeUnits.add("V");
+ typeNames.add("ADC");
+
+ tbut = new UtilityButton("ADC",Font("Small Text", 9, Font::plain));
+ tbut->setEnabledState(true);
+ tbut->setCorners(false,false,false,false);
+ tbut->addListener(this);
+ tbut->setClickingTogglesState(true);
+ tbut->setRadioGroupId(100,dontSendNotification);
+ tbut->setToggleState(false,dontSendNotification);
+ addAndMakeVisible(tbut);
+ typeButtons.add(tbut);
+
+ selectedVoltageRangeValues[DataChannel::HEADSTAGE_CHANNEL] = voltageRanges[DataChannel::HEADSTAGE_CHANNEL][selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL] - 1];
+ selectedVoltageRangeValues[DataChannel::AUX_CHANNEL] = voltageRanges[DataChannel::AUX_CHANNEL][selectedVoltageRange[DataChannel::AUX_CHANNEL] - 1];
+ selectedVoltageRangeValues[DataChannel::ADC_CHANNEL] = voltageRanges[DataChannel::ADC_CHANNEL][selectedVoltageRange[DataChannel::ADC_CHANNEL] - 1];
+
+
+
+ // init channel display skipping options
+ channelDisplaySkipOptions.add("All");
+ channelDisplaySkipOptions.add("2");
+ channelDisplaySkipOptions.add("4");
+ channelDisplaySkipOptions.add("8");
+ channelDisplaySkipOptions.add("16");
+ channelDisplaySkipOptions.add("32");
+ channelDisplaySkipOptions.add("64");
+ selectedChannelDisplaySkip = 1;
+ selectedChannelDisplaySkipValue = channelDisplaySkipOptions[selectedChannelDisplaySkip - 1];
+
+ channelDisplaySkipSelection = new ComboBox("Channel Skip");
+ channelDisplaySkipSelection->addItemList(channelDisplaySkipOptions, 1);
+ channelDisplaySkipSelection->setSelectedId(selectedChannelDisplaySkip, sendNotification);
+ channelDisplaySkipSelection->setEditableText(false);
+ channelDisplaySkipSelection->addListener(this);
+ addAndMakeVisible(channelDisplaySkipSelection);
+
+ channelDisplaySkipLabel = new Label("Channel Display Skip", "Ch. Skip");
+ channelDisplaySkipLabel->setFont(labelFont);
+ channelDisplaySkipLabel->setColour(Label::textColourId, labelColour);
+ addAndMakeVisible(channelDisplaySkipLabel);
+
+
+
+ // init spike raster options
+ spikeRasterSelectionOptions = {"Off", "-50", "-100", "-150", "-200", "-300", "-400", "-500"};
+ selectedSpikeRasterThreshold = 1;
+ selectedSpikeRasterThresholdValue = spikeRasterSelectionOptions[selectedSpikeRasterThreshold - 1];
+
+ spikeRasterSelection = new ComboBox("spikeRasterSelection");
+ spikeRasterSelection->addItemList(spikeRasterSelectionOptions, 1);
+ spikeRasterSelection->setSelectedId(selectedSpikeRasterThreshold, dontSendNotification);
+ spikeRasterSelection->setEditableText(true);
+ spikeRasterSelection->addListener(this);
+ addAndMakeVisible(spikeRasterSelection);
+
+ spikeRasterLabel = new Label("spikeRasterLabel", "Spike Raster Thresh.");
+ spikeRasterLabel->setFont(labelFont);
+ spikeRasterLabel->setColour(Label::textColourId, labelColour);
+ addAndMakeVisible(spikeRasterLabel);
+
+
+
+ // init median offset plotting
+ medianOffsetPlottingLabel = new Label("Median Offset Correction", "Median Offset Correction");
+ medianOffsetPlottingLabel->setFont(labelFont);
+ medianOffsetPlottingLabel->setColour(Label::textColourId, labelColour);
+ addAndMakeVisible(medianOffsetPlottingLabel);
+
+ medianOffsetPlottingButton = new UtilityButton("0", labelFont);
+ medianOffsetPlottingButton->setRadius(5.0f);
+ medianOffsetPlottingButton->setEnabledState(true);
+ medianOffsetPlottingButton->setCorners(true, true, true, true);
+ medianOffsetPlottingButton->addListener(this);
+ medianOffsetPlottingButton->setClickingTogglesState(true);
+ medianOffsetPlottingButton->setToggleState(false, sendNotification);
+ addAndMakeVisible(medianOffsetPlottingButton);
+
+ //init channel name toggle
+ showChannelNumberLabel = new Label("showcChannelLabel", "Show channel number instead of name");
+ showChannelNumberLabel->setFont(labelFont);
+ showChannelNumberLabel->setColour(Label::textColourId, labelColour);
+ addAndMakeVisible(showChannelNumberLabel);
+
+ showChannelNumberButton = new UtilityButton("0", labelFont);
+ showChannelNumberButton->setRadius(5.0f);
+ showChannelNumberButton->setEnabledState(true);
+ showChannelNumberButton->setCorners(true, true, true, true);
+ showChannelNumberButton->addListener(this);
+ showChannelNumberButton->setClickingTogglesState(true);
+ showChannelNumberButton->setToggleState(false, sendNotification);
+ addAndMakeVisible(showChannelNumberButton);
+
+
+ // init show/hide options button
+ showHideOptionsButton = new ShowHideOptionsButton(this);
+ showHideOptionsButton->addListener(this);
+ addAndMakeVisible(showHideOptionsButton);
+
+ // init timebases options
+ timebases.add("0.25");
+ timebases.add("0.5");
+ timebases.add("1.0");
+ timebases.add("2.0");
+ timebases.add("3.0");
+ timebases.add("4.0");
+ timebases.add("5.0");
+ timebases.add("10.0");
+ timebases.add("20.0");
+ selectedTimebase = 4;
+ selectedTimebaseValue = timebases[selectedTimebase-1];
+
+ spreads.add("10");
+ spreads.add("20");
+ spreads.add("30");
+ spreads.add("40");
+ spreads.add("50");
+ spreads.add("60");
+ spreads.add("70");
+ spreads.add("80");
+ spreads.add("90");
+ spreads.add("100");
+ selectedSpread = 4;
+ selectedSpreadValue = spreads[selectedSpread-1];
+
+
+ overlaps.add("0.5");
+ overlaps.add("0.75");
+ overlaps.add("1");
+ overlaps.add("2");
+ overlaps.add("3");
+ overlaps.add("4");
+ overlaps.add("5");
+ selectedOverlap = 4;
+ selectedOverlapValue = overlaps[selectedOverlap-1];
+
+ saturationThresholds.add("0.5");
+ saturationThresholds.add("100");
+ saturationThresholds.add("1000");
+ saturationThresholds.add("5000");
+ saturationThresholds.add("6389");
+
+ selectedSaturation = 5;
+ selectedSaturationValue = saturationThresholds[selectedSaturation-1];
+
+
+ colorGroupings.add("1");
+ colorGroupings.add("2");
+ colorGroupings.add("4");
+ colorGroupings.add("8");
+ colorGroupings.add("16");
+
+
+ rangeSelection = new ComboBox("Voltage range");
+ rangeSelection->addItemList(voltageRanges[DataChannel::HEADSTAGE_CHANNEL], 1);
+ rangeSelection->setSelectedId(selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL], sendNotification);
+ rangeSelection->setEditableText(true);
+ rangeSelection->addListener(this);
+ addAndMakeVisible(rangeSelection);
+
+
+ timebaseSelection = new ComboBox("Timebase");
+ timebaseSelection->addItemList(timebases, 1);
+ timebaseSelection->setSelectedId(selectedTimebase, sendNotification);
+ timebaseSelection->setEditableText(true);
+ timebaseSelection->addListener(this);
+ addAndMakeVisible(timebaseSelection);
+
+
+ spreadSelection = new ComboBox("Spread");
+ spreadSelection->addItemList(spreads, 1);
+ spreadSelection->setSelectedId(selectedSpread,sendNotification);
+ spreadSelection->addListener(this);
+ spreadSelection->setEditableText(true);
+ addAndMakeVisible(spreadSelection);
+
+ overlapSelection = new ComboBox("Overlap");
+ overlapSelection->addItemList(overlaps, 1);
+ overlapSelection->setSelectedId(selectedOverlap,sendNotification);
+ overlapSelection->addListener(this);
+ overlapSelection->setEditableText(true);
+ addAndMakeVisible(overlapSelection);
+
+ saturationWarningSelection = new ComboBox("Sat.Warn");
+ saturationWarningSelection->addItemList(saturationThresholds, 1);
+ saturationWarningSelection->setSelectedId(selectedSaturation,sendNotification);
+ saturationWarningSelection->addListener(this);
+ saturationWarningSelection->setEditableText(true);
+ addAndMakeVisible(saturationWarningSelection);
+
+
+ colorGroupingSelection = new ComboBox("Color Grouping");
+ colorGroupingSelection->addItemList(colorGroupings, 1);
+ colorGroupingSelection->setSelectedId(1,sendNotification);
+ colorGroupingSelection->addListener(this);
+ addAndMakeVisible(colorGroupingSelection);
+
+ invertInputButton = new UtilityButton("Invert", Font("Small Text", 13, Font::plain));
+ invertInputButton->setRadius(5.0f);
+ invertInputButton->setEnabledState(true);
+ invertInputButton->setCorners(true, true, true, true);
+ invertInputButton->addListener(this);
+ invertInputButton->setClickingTogglesState(true);
+ invertInputButton->setToggleState(false, sendNotification);
+ addAndMakeVisible(invertInputButton);
+
+ // toggle button to reverse the order of channels
+ reverseChannelsDisplayButton = new UtilityButton("0", labelFont);
+ reverseChannelsDisplayButton->setRadius(5.0f);
+ reverseChannelsDisplayButton->setEnabledState(true);
+ reverseChannelsDisplayButton->setCorners(true, true, true, true);
+ reverseChannelsDisplayButton->addListener(this);
+ reverseChannelsDisplayButton->setClickingTogglesState(true);
+ reverseChannelsDisplayButton->setToggleState(lfpDisplay->getChannelsReversed(), sendNotification);
+ addAndMakeVisible(reverseChannelsDisplayButton);
+
+ reverseChannelsDisplayLabel = new Label("Rev. Channels", "Rev. Channels");
+ reverseChannelsDisplayLabel->setFont(labelFont);
+ reverseChannelsDisplayLabel->setColour(Label::textColourId, labelColour);
+ addAndMakeVisible(reverseChannelsDisplayLabel);
+
+
+ //button for controlling drawing algorithm - old line-style or new per-pixel style
+ drawMethodButton = new UtilityButton("DrawMethod", Font("Small Text", 13, Font::plain));
+ drawMethodButton->setRadius(5.0f);
+ drawMethodButton->setEnabledState(true);
+ drawMethodButton->setCorners(true, true, true, true);
+ drawMethodButton->addListener(this);
+ drawMethodButton->setClickingTogglesState(true);
+ drawMethodButton->setToggleState(false, sendNotification);
+ addAndMakeVisible(drawMethodButton);
+
+ // two sliders for the two histogram components of the supersampled plotting mode
+ // todo: rename these
+ brightnessSliderA = new Slider();
+ brightnessSliderA->setRange (0, 1);
+ brightnessSliderA->setTextBoxStyle(Slider::NoTextBox, false, 50,30);
+ brightnessSliderA->addListener(this);
+ addAndMakeVisible (brightnessSliderA);
+
+ brightnessSliderB = new Slider;
+ brightnessSliderB->setRange (0, 1);
+ brightnessSliderB->setTextBoxStyle(Slider::NoTextBox, false, 50,30);
+ brightnessSliderB->addListener(this);
+ addAndMakeVisible (brightnessSliderB);
+
+ sliderALabel = new Label("Brightness","Brightness");
+ sliderALabel->setFont(Font("Small Text", 13, Font::plain));
+ sliderALabel->setColour(Label::textColourId,Colour(150,150,150));
+ addAndMakeVisible(sliderALabel);
+
+ sliderBLabel = new Label("Min. brightness","Min. brightness");
+ sliderBLabel->setFont(Font("Small Text", 13, Font::plain));
+ sliderBLabel->setColour(Label::textColourId,Colour(150,150,150));
+ addAndMakeVisible(sliderBLabel);
+
+
+ //ScopedPointer drawClipWarningButton; // optinally draw (subtle) warning if data is clipped in display
+ drawClipWarningButton = new UtilityButton("0", Font("Small Text", 13, Font::plain));
+ drawClipWarningButton->setRadius(5.0f);
+ drawClipWarningButton->setEnabledState(true);
+ drawClipWarningButton->setCorners(true, true, true, true);
+ drawClipWarningButton->addListener(this);
+ drawClipWarningButton->setClickingTogglesState(true);
+ drawClipWarningButton->setToggleState(false, sendNotification);
+ addAndMakeVisible(drawClipWarningButton);
+
+
+ //ScopedPointer drawSaturateWarningButton; // optionally raise hell if the actual data is saturating
+ drawSaturateWarningButton = new UtilityButton("0", Font("Small Text", 13, Font::plain));
+ drawSaturateWarningButton->setRadius(5.0f);
+ drawSaturateWarningButton->setEnabledState(true);
+ drawSaturateWarningButton->setCorners(true, true, true, true);
+ drawSaturateWarningButton->addListener(this);
+ drawSaturateWarningButton->setClickingTogglesState(true);
+ drawSaturateWarningButton->setToggleState(false, sendNotification);
+ addAndMakeVisible(drawSaturateWarningButton);
+
+
+ //button for pausing the display - works by skipping buffer updates. This way scrolling etc still works
+ pauseButton = new UtilityButton("Pause", Font("Small Text", 13, Font::plain));
+ pauseButton->setRadius(5.0f);
+ pauseButton->setEnabledState(true);
+ pauseButton->setCorners(true, true, true, true);
+ pauseButton->addListener(this);
+ pauseButton->setClickingTogglesState(true);
+ pauseButton->setToggleState(false, sendNotification);
+ addAndMakeVisible(pauseButton);
+
+ // add event display-specific controls (currently just an enable/disable button)
+ for (int i = 0; i < 8; i++)
+ {
+
+ EventDisplayInterface* eventOptions = new EventDisplayInterface(lfpDisplay, canvas, i);
+ eventDisplayInterfaces.add(eventOptions);
+ addAndMakeVisible(eventOptions);
+ eventOptions->setBounds(700+(floor(i/2)*20), getHeight()-20-(i%2)*20, 40, 20);
+
+ lfpDisplay->setEventDisplayState(i,true);
+
+ }
+
+ lfpDisplay->setRange(voltageRanges[DataChannel::HEADSTAGE_CHANNEL][selectedVoltageRange[DataChannel::HEADSTAGE_CHANNEL] - 1].getFloatValue()*rangeGain[DataChannel::HEADSTAGE_CHANNEL]
+ , DataChannel::HEADSTAGE_CHANNEL);
+ lfpDisplay->setRange(voltageRanges[DataChannel::ADC_CHANNEL][selectedVoltageRange[DataChannel::ADC_CHANNEL] - 1].getFloatValue()*rangeGain[DataChannel::ADC_CHANNEL]
+ , DataChannel::ADC_CHANNEL);
+ lfpDisplay->setRange(voltageRanges[DataChannel::AUX_CHANNEL][selectedVoltageRange[DataChannel::AUX_CHANNEL] - 1].getFloatValue()*rangeGain[DataChannel::AUX_CHANNEL]
+ , DataChannel::AUX_CHANNEL);
+
+}
+
+LfpDisplayOptions::~LfpDisplayOptions()
+{
+
+}
+
+void LfpDisplayOptions::resized()
+{
+ rangeSelection->setBounds(5,getHeight()-30,80,25);
+ timebaseSelection->setBounds(175,getHeight()-30,60,25);
+
+ spreadSelection->setBounds(5,getHeight()-90,60,25);
+
+ overlapSelection->setBounds(100,getHeight()-90,60,25);
+
+ drawClipWarningButton->setBounds(175,getHeight()-89,20,20);
+ drawSaturateWarningButton->setBounds(325, getHeight()-89, 20, 20);
+
+ colorGroupingSelection->setBounds(400,getHeight()-90,60,25);
+
+ invertInputButton->setBounds(35,getHeight()-190,100,22);
+ drawMethodButton->setBounds(35,getHeight()-160,100,22);
+
+ pauseButton->setBounds(450,getHeight()-50,50,44);
+
+
+ // Reverse Channels Display
+ reverseChannelsDisplayButton->setBounds(pauseButton->getRight() + 5,
+ getHeight() - 50,
+ 20,
+ 20);
+ reverseChannelsDisplayLabel->setBounds(reverseChannelsDisplayButton->getRight(),
+ reverseChannelsDisplayButton->getY(),
+ 120,
+ 22);
+
+ // Channel Display Skip Selector
+ channelDisplaySkipSelection->setBounds(reverseChannelsDisplayButton->getX(),
+ reverseChannelsDisplayButton->getBottom(),
+ 60,
+ 25);
+ channelDisplaySkipLabel->setBounds(channelDisplaySkipSelection->getRight(),
+ channelDisplaySkipSelection->getY() + 2,
+ 100,
+ 22);
+
+ // Median Offset Plotting Button
+ medianOffsetPlottingButton->setBounds(reverseChannelsDisplayLabel->getRight() + 5,
+ reverseChannelsDisplayButton->getY(),
+ 20,
+ 20);
+ medianOffsetPlottingLabel->setBounds(medianOffsetPlottingButton->getRight(),
+ medianOffsetPlottingButton->getY(),
+ 150,
+ 22);
+
+ //Channel name toggle
+ showChannelNumberButton->setBounds(medianOffsetPlottingLabel->getRight() + 5,
+ medianOffsetPlottingLabel->getY(),
+ 20,
+ 20);
+ showChannelNumberLabel->setBounds(showChannelNumberButton->getRight(),
+ showChannelNumberButton->getY(),
+ 200,
+ 22);
+
+ // Spike raster plotting button
+ spikeRasterSelection->setBounds(medianOffsetPlottingButton->getX(),
+ medianOffsetPlottingButton->getBottom(),
+ 60,
+ 25);
+ spikeRasterLabel->setBounds(spikeRasterSelection->getRight(),
+ spikeRasterSelection->getY(),
+ 120,
+ 22);
+
+
+ // Saturation Warning Selection
+ saturationWarningSelection->setBounds(250, getHeight()-90, 60, 25);
+
+
+ for (int i = 0; i < 8; i++)
+ {
+ eventDisplayInterfaces[i]->setBounds(300+(floor(i/2)*20), getHeight()-40+(i%2)*20, 40, 20); // arrange event channel buttons in two rows
+ eventDisplayInterfaces[i]->repaint();
+ }
+
+ brightnessSliderA->setBounds(170,getHeight()-190,100,22);
+ sliderALabel->setBounds(270, getHeight()-190, 180, 22);
+ brightnessSliderA->setValue(0.9); //set default value
+
+ brightnessSliderB->setBounds(170,getHeight()-160,100,22);
+ sliderBLabel->setBounds(270, getHeight()-160, 180, 22);
+ brightnessSliderB->setValue(0.1); //set default value
+
+ showHideOptionsButton->setBounds (getWidth() - 28, getHeight() - 28, 20, 20);
+
+ int bh = 25/typeButtons.size();
+ for (int i = 0; i < typeButtons.size(); i++)
+ {
+ typeButtons[i]->setBounds(95,getHeight()-30+i*bh,50,bh);
+ }
+
+
+ colourSchemeOptionLabel->setBounds(medianOffsetPlottingButton->getX(),
+ getHeight()-190,
+ 100,
+ 22);
+ colourSchemeOptionSelection->setBounds(colourSchemeOptionLabel->getRight(),
+ colourSchemeOptionLabel->getY(),
+ 80,
+ 25);
+
+ // set the size of the active colour scheme's options, if it has configurable options
+ if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
+ {
+ lfpDisplay->getColourSchemePtr()->setBounds(colourSchemeOptionLabel->getX(),
+ colourSchemeOptionLabel->getBottom(),
+ 200,
+ 110);
+ }
+}
+
+void LfpDisplayOptions::paint(Graphics& g)
+{
+ int row1 = 55;
+ int row2 = 110;
+
+ g.fillAll(Colours::black);
+ g.setFont(Font("Default", 16, Font::plain));
+
+ g.setColour(Colour(100,100,100));
+
+ g.drawText("Range("+ rangeUnits[selectedChannelType] +")",5,getHeight()-row1,300,20,Justification::left, false);
+ g.drawText("Timebase(s)",160,getHeight()-row1,300,20,Justification::left, false);
+ g.drawText("Size(px)",5,getHeight()-row2,300,20,Justification::left, false);
+ g.drawText("Clip",100,getHeight()-row2,300,20,Justification::left, false);
+ g.drawText("Warn",168,getHeight()-row2,300,20,Justification::left, false);
+
+ g.drawText("Sat. Warning",225,getHeight()-row2,300,20,Justification::left, false);
+
+ g.drawText("Color grouping",365,getHeight()-row2,300,20,Justification::left, false);
+
+ g.drawText("Event disp.",300,getHeight()-row1,300,20,Justification::left, false);
+
+ if(canvas->drawClipWarning)
+ {
+ g.setColour(Colours::white);
+ g.fillRoundedRectangle(173,getHeight()-90-1,24,24,6.0f);
+ }
+
+ if(canvas->drawSaturationWarning)
+ {
+ g.setColour(Colours::red);
+ g.fillRoundedRectangle(323,getHeight()-90-1,24,24,6.0f);
+ }
+
+}
+
+int LfpDisplayOptions::getChannelHeight()
+{
+ return (int)spreadSelection->getText().getIntValue();
+}
+
+
+bool LfpDisplayOptions::getDrawMethodState()
+{
+
+ return drawMethodButton->getToggleState();
+}
+
+bool LfpDisplayOptions::getInputInvertedState()
+{
+ return invertInputButton->getToggleState();
+}
+
+bool LfpDisplayOptions::getChannelNameState()
+{
+ return showChannelNumberButton->getToggleState();
+}
+
+bool LfpDisplayOptions::getDisplaySpikeRasterizerState()
+{
+// return spikeRasterButton->getToggleState();
+ return false;
+}
+
+void LfpDisplayOptions::setDisplaySpikeRasterizerState(bool isEnabled)
+{
+// spikeRasterButton->setToggleState(isEnabled, dontSendNotification);
+
+// if (isEnabled) medianOffsetPlottingButton->setToggleState(true, sendNotification);
+}
+
+void LfpDisplayOptions::setRangeSelection(float range, bool canvasMustUpdate)
+{
+ if (canvasMustUpdate)
+ {
+ rangeSelection->setText(String(range/rangeGain[selectedChannelType]), sendNotification);
+ }
+ else
+ {
+ rangeSelection->setText(String(range/rangeGain[selectedChannelType]),dontSendNotification);
+
+ selectedVoltageRange[selectedChannelType] = rangeSelection->getSelectedId();
+ selectedVoltageRangeValues[selectedChannelType] = rangeSelection->getText();
+
+ canvas->repaint();
+ canvas->refresh();
+ }
+
+}
+
+void LfpDisplayOptions::setSpreadSelection(int spread, bool canvasMustUpdate, bool deferDisplayRefresh)
+{
+
+ if (canvasMustUpdate)
+ {
+ spreadSelection->setText(String(spread),sendNotification);
+ }
+ else
+ {
+ spreadSelection->setText(String(spread),dontSendNotification);
+ selectedSpread = spreadSelection->getSelectedId();
+ selectedSpreadValue = spreadSelection->getText();
+
+ if (!deferDisplayRefresh)
+ {
+ canvas->repaint();
+ canvas->refresh();
+ }
+ }
+}
+
+void LfpDisplayOptions::togglePauseButton(bool sendUpdate)
+{
+ pauseButton->setToggleState(!pauseButton->getToggleState(), sendUpdate ? sendNotification : dontSendNotification);
+}
+
+void LfpDisplayOptions::buttonClicked(Button* b)
+{
+ if (b == invertInputButton)
+ {
+ lfpDisplay->setInputInverted(b->getToggleState());
+ return;
+ }
+ if (b == reverseChannelsDisplayButton)
+ {
+ lfpDisplay->setChannelsReversed(b->getToggleState());
+ return;
+ }
+ if (b == medianOffsetPlottingButton)
+ {
+ if (lfpDisplay->getSpikeRasterPlotting())
+ {
+ medianOffsetPlottingButton->setToggleState(true, dontSendNotification);
+ }
+ else
+ {
+ lfpDisplay->setMedianOffsetPlotting(b->getToggleState());
+ }
+ return;
+ }
+ if (b == drawMethodButton)
+ {
+ lfpDisplay->setDrawMethod(b->getToggleState()); // this should be done the same way as drawClipWarning - or the other way around.
+
+ return;
+ }
+ if (b == drawClipWarningButton)
+ {
+ canvas->drawClipWarning = b->getToggleState();
+ canvas->redraw();
+ return;
+ }
+ if (b == drawSaturateWarningButton)
+ {
+ canvas->drawSaturationWarning = b->getToggleState();
+ canvas->redraw();
+ return;
+ }
+
+ if (b == pauseButton)
+ {
+ lfpDisplay->isPaused = b->getToggleState();
+ return;
+ }
+
+ if (b == showHideOptionsButton)
+ {
+ canvas->toggleOptionsDrawer(b->getToggleState());
+ }
+
+ if (b == showChannelNumberButton)
+ {
+ int numChannels = lfpDisplay->channelInfo.size();
+ for (int i = 0; i < numChannels; ++i)
+ {
+ lfpDisplay->channelInfo[i]->repaint();
+ }
+ return;
+ }
+
+ int idx = typeButtons.indexOf((UtilityButton*)b);
+
+ if ((idx >= 0) && (b->getToggleState()))
+ {
+ for (int i = 0; i < lfpDisplay->getNumChannels(); i++)
+ {
+ if (lfpDisplay->channels[i]->getSelected())
+ {
+ lfpDisplay->channels[i]->deselect();
+ lfpDisplay->channels[i]->repaint();
+ }
+ }
+
+ setSelectedType((DataChannel::DataChannelTypes) idx, false);
+ }
+
+}
+
+void LfpDisplayOptions::setTimebaseAndSelectionText(float timebase)
+{
+ canvas->timebase = timebase;
+
+ if (canvas->timebase) // if timebase != 0
+ {
+ if (canvas->timebase < timebases[0].getFloatValue())
+ {
+ timebaseSelection->setSelectedId(1, dontSendNotification);
+ canvas->timebase = timebases[0].getFloatValue();
+ }
+ else if (canvas->timebase > timebases[timebases.size()-1].getFloatValue())
+ {
+ timebaseSelection->setSelectedId(timebases.size(), dontSendNotification);
+ canvas->timebase = timebases[timebases.size()-1].getFloatValue();
+ }
+ else{
+ timebaseSelection->setText(String(canvas->timebase, 1), dontSendNotification);
+ }
+ }
+ else
+ {
+ if (selectedSpread == 0)
+ {
+ timebaseSelection->setText(selectedTimebaseValue, dontSendNotification);
+ canvas->timebase = selectedTimebaseValue.getFloatValue();
+ }
+ else
+ {
+ timebaseSelection->setSelectedId(selectedTimebase,dontSendNotification);
+ canvas->timebase = timebases[selectedTimebase-1].getFloatValue();
+ }
+
+ }
+}
+
+
+void LfpDisplayOptions::comboBoxChanged(ComboBox* cb)
+{
+ if (canvas->getNumChannels() == 0) return;
+
+ if (cb == channelDisplaySkipSelection)
+ {
+ const int skipAmt = pow(2, cb->getSelectedId() - 1);
+ lfpDisplay->setChannelDisplaySkipAmount(skipAmt);
+ }
+ else if (cb == spikeRasterSelection)
+ {
+ // if custom value
+ if (cb->getSelectedId() == 0)
+ {
+ auto val = fabsf(cb->getText().getFloatValue());
+
+ if (val == 0) // if value is zero, just disable plotting and set text to "Off"
+ {
+ cb->setSelectedItemIndex(0, dontSendNotification);
+ lfpDisplay->setSpikeRasterPlotting(false);
+ return;
+ }
+
+ if (val > 500)
+ {
+ val = 500;
+ }
+
+ val *= -1;
+
+ spikeRasterSelection->setText(String(val), dontSendNotification);
+ lfpDisplay->setSpikeRasterThreshold(val);
+ medianOffsetPlottingButton->setToggleState(true, dontSendNotification);
+ lfpDisplay->setMedianOffsetPlotting(true);
+ lfpDisplay->setSpikeRasterPlotting(true);
+ }
+ else if (cb->getSelectedItemIndex() == 0) // if "Off"
+ {
+ lfpDisplay->setSpikeRasterPlotting(false);
+ return;
+ }
+ else
+ {
+ auto val = cb->getText().getFloatValue();
+
+ lfpDisplay->setSpikeRasterThreshold(val);
+ medianOffsetPlottingButton->setToggleState(true, dontSendNotification);
+ lfpDisplay->setMedianOffsetPlotting(true);
+ lfpDisplay->setSpikeRasterPlotting(true);
+ }
+ }
+ else if (cb == colourSchemeOptionSelection)
+ {
+ // hide the old colour scheme config options if they are displayed
+ if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
+ removeChildComponent(lfpDisplay->getColourSchemePtr());
+
+ // change the active colour scheme ptr
+ lfpDisplay->setActiveColourSchemeIdx(cb->getSelectedId()-1);
+
+ // show the new colour scheme's config options if has any
+
+ if (lfpDisplay->getColourSchemePtr()->hasConfigurableElements())
+ {
+ lfpDisplay->getColourSchemePtr()->setBounds(colourSchemeOptionLabel->getX(),
+ colourSchemeOptionLabel->getBottom(),
+ 200,
+ 110);
+ addAndMakeVisible(lfpDisplay->getColourSchemePtr());
+ }
+
+ // update the lfpDisplay's colors and redraw
+ lfpDisplay->setColors();
+ canvas->redraw();
+ }
+ else if (cb == timebaseSelection)
+ {
+ if (cb->getSelectedId())
+ {
+ canvas->timebase = timebases[cb->getSelectedId()-1].getFloatValue();
+ }
+ else
+ {
+ setTimebaseAndSelectionText(cb->getText().getFloatValue());
+ }
+ }
+ else if (cb == rangeSelection)
+ {
+ if (cb->getSelectedId())
+ {
+ lfpDisplay->setRange(voltageRanges[selectedChannelType][cb->getSelectedId()-1].getFloatValue()*rangeGain[selectedChannelType]
+ ,selectedChannelType);
+ }
+ else
+ {
+ float vRange = cb->getText().getFloatValue();
+ if (vRange)
+ {
+ if (vRange < voltageRanges[selectedChannelType][0].getFloatValue())
+ {
+ cb->setSelectedId(1,dontSendNotification);
+ vRange = voltageRanges[selectedChannelType][0].getFloatValue();
+ }
+ else if (vRange > voltageRanges[selectedChannelType][voltageRanges[selectedChannelType].size()-1].getFloatValue())
+ {
+ // cb->setSelectedId(voltageRanges[selectedChannelType].size(),dontSendNotification);
+ // vRange = voltageRanges[selectedChannelType][voltageRanges[selectedChannelType].size()-1].getFloatValue();
+ }
+ else
+ {
+ if (rangeGain[selectedChannelType] > 1)
+ cb->setText(String(vRange,1),dontSendNotification);
+ else
+ cb->setText(String(vRange),dontSendNotification);
+ }
+ lfpDisplay->setRange(vRange*rangeGain[selectedChannelType],selectedChannelType);
+ }
+ else
+ {
+ if (selectedVoltageRange[selectedChannelType])
+ cb->setText(selectedVoltageRangeValues[selectedChannelType],dontSendNotification);
+ else
+ cb->setSelectedId(selectedVoltageRange[selectedChannelType],dontSendNotification);
+ }
+ }
+ selectedVoltageRange[selectedChannelType] = cb->getSelectedId();
+ selectedVoltageRangeValues[selectedChannelType] = cb->getText();
+ //std::cout << "Setting range to " << voltageRanges[cb->getSelectedId()-1].getFloatValue() << std::endl;
+ canvas->redraw();
+ }
+ else if (cb == spreadSelection)
+ {
+
+ if (cb->getSelectedId())
+ {
+ if (lfpDisplay->getSingleChannelState())
+ {
+ lfpDisplay->cacheNewChannelHeight(spreads[cb->getSelectedId()-1].getIntValue());
+ }
+ else
+ {
+ lfpDisplay->setChannelHeight(spreads[cb->getSelectedId()-1].getIntValue());
+ resized();
+ }
+ }
+ else
+ {
+ int spread = cb->getText().getIntValue();
+ if (spread)
+ {
+ if (spread < spreads[0].getFloatValue())
+ {
+ cb->setSelectedId(1,dontSendNotification);
+ spread = spreads[0].getFloatValue();
+ }
+ else if (spread > spreads[spreads.size()-1].getFloatValue())
+ {
+ cb->setSelectedId(spreads.size(),dontSendNotification);
+ spread = spreads[spreads.size()-1].getFloatValue();
+ }
+ else
+ {
+ cb->setText(String(spread),dontSendNotification);
+ }
+
+ // if single channel focus is on, cache the value
+ if (lfpDisplay->getSingleChannelState())
+ {
+ lfpDisplay->cacheNewChannelHeight(spread);
+ }
+ else
+ {
+ lfpDisplay->setChannelHeight(spread);
+ canvas->resized();
+ }
+ }
+ else
+ {
+ if (selectedSpread == 0)
+ cb->setText(selectedSpreadValue,dontSendNotification);
+ else
+ cb->setSelectedId(selectedSpread,dontSendNotification);
+ }
+ }
+ selectedSpread = cb->getSelectedId();
+ selectedSpreadValue = cb->getText();
+
+ if (!lfpDisplay->getSingleChannelState()) canvas->redraw();
+ //std::cout << "Setting spread to " << spreads[cb->getSelectedId()-1].getFloatValue() << std::endl;
+ }
+ else if (cb == saturationWarningSelection)
+ {
+ if (cb->getSelectedId())
+ {
+ selectedSaturationValueFloat = (saturationThresholds[cb->getSelectedId()-1].getFloatValue());
+ }
+ else
+ {
+ selectedSaturationValueFloat = cb->getText().getFloatValue();
+ if (selectedSaturationValueFloat)
+ {
+ std::cout << "Setting saturation warning to to " << selectedSaturationValueFloat << std::endl;
+ if (selectedSaturationValueFloat < 0)
+ {
+ cb->setSelectedId(1,dontSendNotification);
+ selectedSaturationValueFloat = saturationThresholds[0].getFloatValue();
+ }
+ else
+ {
+ // cb->setText(String(selectedSaturationValueFloat),dontSendNotification);
+ }
+ }
+ else
+ {
+ // cb->setSelectedId(1,dontSendNotification);
+ //selectedSaturationValueFloat = saturationThresholds[0].getFloatValue();
+
+ }
+ }
+ canvas->redraw();
+
+ std::cout << "Setting saturation warning to to " << selectedSaturationValueFloat << std::endl;
+ }
+ else if (cb == overlapSelection)
+ {
+ if (cb->getSelectedId())
+ {
+ canvas->channelOverlapFactor = (overlaps[cb->getSelectedId()-1].getFloatValue());
+ canvas->resized();
+ }
+ else
+ {
+ float overlap = cb->getText().getFloatValue();
+ if (overlap)
+ {
+ if (overlap < overlaps[0].getFloatValue())
+ {
+ cb->setSelectedId(1,dontSendNotification);
+ overlap = overlaps[0].getFloatValue();
+ }
+ else if (overlap > overlaps[overlaps.size()-1].getFloatValue())
+ {
+ cb->setSelectedId(overlaps.size(),dontSendNotification);
+ overlap = overlaps[overlaps.size()-1].getFloatValue();
+ }
+ else
+ {
+ cb->setText(String(overlap),dontSendNotification);
+ }
+ canvas->channelOverlapFactor= overlap;
+ canvas->resized();
+ }
+ else
+ {
+ if (selectedSpread == 0)
+ cb->setText(selectedSpreadValue,dontSendNotification);
+ else
+ cb->setSelectedId(selectedSpread,dontSendNotification);
+ }
+ }
+ selectedSpread = cb->getSelectedId();
+ selectedSpreadValue = cb->getText();
+ lfpDisplay->setChannelHeight( lfpDisplay->getChannelHeight());
+ canvas->redraw();
+ //std::cout << "Setting spread to " << spreads[cb->getSelectedId()-1].getFloatValue() << std::endl;
+ }
+
+ else if (cb == colorGroupingSelection)
+ {
+ // set color grouping here
+ lfpDisplay->setColorGrouping(colorGroupings[cb->getSelectedId()-1].getIntValue());// so that channel colors get re-assigned
+ canvas->redraw();
+ }
+
+ timescale->setTimebase(canvas->timebase);
+}
+
+
+void LfpDisplayOptions::sliderValueChanged(Slider* sl)
+{
+ if (sl == brightnessSliderA)
+ canvas->histogramParameterA = sl->getValue();
+
+ if (sl == brightnessSliderB)
+ canvas->histogramParameterB = sl->getValue();
+
+
+ canvas->fullredraw=true;
+ //repaint();
+ canvas->refresh();
+
+}
+
+void LfpDisplayOptions::sliderEvent(Slider* sl) {}
+
+DataChannel::DataChannelTypes LfpDisplayOptions::getChannelType(int n)
+{
+ if (n < processor->getNumInputs())
+ return processor->getDataChannel(n)->getChannelType();
+ else
+ return DataChannel::HEADSTAGE_CHANNEL;
+}
+
+DataChannel::DataChannelTypes LfpDisplayOptions::getSelectedType()
+{
+ return selectedChannelType;
+}
+
+void LfpDisplayOptions::setSelectedType(DataChannel::DataChannelTypes type, bool toggleButton)
+{
+ if (selectedChannelType == type)
+ return; //Nothing to do here
+ selectedChannelType = type;
+ rangeSelection->clear(dontSendNotification);
+ rangeSelection->addItemList(voltageRanges[type],1);
+
+ int id = selectedVoltageRange[type];
+ if (id)
+ rangeSelection->setSelectedId(id,sendNotification);
+ else
+ rangeSelection->setText(selectedVoltageRangeValues[selectedChannelType],dontSendNotification);
+
+ repaint(5,getHeight()-55,300,100);
+
+ if (toggleButton)
+ typeButtons[type]->setToggleState(true,dontSendNotification);
+}
+
+String LfpDisplayOptions::getTypeName(DataChannel::DataChannelTypes type)
+{
+ return typeNames[type];
+}
+
+int LfpDisplayOptions::getRangeStep(DataChannel::DataChannelTypes type)
+{
+ return rangeSteps[type];
+}
+
+
+
+void LfpDisplayOptions::saveParameters(XmlElement* xml)
+{
+ // TODO: (kelly) add savers for:
+ // - channel reverse
+ // - channel zoom slider
+ // - channel display skip
+ std::cout << "Saving lfp display params" << std::endl;
+
+ XmlElement* xmlNode = xml->createNewChildElement("LFPDISPLAY");
+
+ lfpDisplay->reactivateChannels();
+
+ xmlNode->setAttribute("Range",selectedVoltageRangeValues[0]+","+selectedVoltageRangeValues[1]+
+ ","+selectedVoltageRangeValues[2]);
+ xmlNode->setAttribute("Timebase",timebaseSelection->getText());
+ xmlNode->setAttribute("Spread",spreadSelection->getText());
+ xmlNode->setAttribute("colorGrouping",colorGroupingSelection->getSelectedId());
+ xmlNode->setAttribute("isInverted",invertInputButton->getToggleState());
+ xmlNode->setAttribute("drawMethod",drawMethodButton->getToggleState());
+
+ int eventButtonState = 0;
+
+ for (int i = 0; i < 8; i++)
+ {
+ if (lfpDisplay->eventDisplayEnabled[i])
+ {
+ eventButtonState += (1 << i);
+ }
+ }
+
+ lfpDisplay->reactivateChannels();
+
+ xmlNode->setAttribute("EventButtonState", eventButtonState);
+
+ String channelDisplayState = "";
+
+ for (int i = 0; i < canvas->nChans; i++)
+ {
+ if (lfpDisplay->getEnabledState(i))
+ {
+ channelDisplayState += "1";
+ }
+ else
+ {
+ channelDisplayState += "0";
+ }
+ //std::cout << channelDisplayState;
+ }
+
+ //std::cout << std::endl;
+
+
+ xmlNode->setAttribute("ChannelDisplayState", channelDisplayState);
+
+ xmlNode->setAttribute("ScrollX",canvas->viewport->getViewPositionX());
+ xmlNode->setAttribute("ScrollY",canvas->viewport->getViewPositionY());
+}
+
+
+void LfpDisplayOptions::loadParameters(XmlElement* xml)
+{
+ // TODO: (kelly) add loaders for:
+ // - channel reverse
+ // - channel zoom slider
+ // - channel display skip
+ forEachXmlChildElement(*xml, xmlNode)
+ {
+ if (xmlNode->hasTagName("LFPDISPLAY"))
+ {
+ StringArray ranges;
+ ranges.addTokens(xmlNode->getStringAttribute("Range"),",",String::empty);
+ selectedVoltageRangeValues[0] = ranges[0];
+ selectedVoltageRangeValues[1] = ranges[1];
+ selectedVoltageRangeValues[2] = ranges[2];
+ selectedVoltageRange[0] = voltageRanges[0].indexOf(ranges[0])+1;
+ selectedVoltageRange[1] = voltageRanges[1].indexOf(ranges[1])+1;
+ selectedVoltageRange[2] = voltageRanges[2].indexOf(ranges[2])+1;
+ rangeSelection->setText(ranges[0]);
+
+ timebaseSelection->setText(xmlNode->getStringAttribute("Timebase"));
+ spreadSelection->setText(xmlNode->getStringAttribute("Spread"));
+ if (xmlNode->hasAttribute("colorGrouping"))
+ {
+ colorGroupingSelection->setSelectedId(xmlNode->getIntAttribute("colorGrouping"));
+ }
+ else
+ {
+ colorGroupingSelection->setSelectedId(1);
+ }
+
+ invertInputButton->setToggleState(xmlNode->getBoolAttribute("isInverted", true), sendNotification);
+
+ drawMethodButton->setToggleState(xmlNode->getBoolAttribute("drawMethod", true), sendNotification);
+
+ canvas->viewport->setViewPosition(xmlNode->getIntAttribute("ScrollX"),
+ xmlNode->getIntAttribute("ScrollY"));
+
+ int eventButtonState = xmlNode->getIntAttribute("EventButtonState");
+
+ for (int i = 0; i < 8; i++)
+ {
+ lfpDisplay->eventDisplayEnabled[i] = (eventButtonState >> i) & 1;
+
+ eventDisplayInterfaces[i]->checkEnabledState();
+ }
+
+ String channelDisplayState = xmlNode->getStringAttribute("ChannelDisplayState");
+
+ for (int i = 0; i < channelDisplayState.length(); i++)
+ {
+
+ if (channelDisplayState.substring(i,i+1).equalsIgnoreCase("1"))
+ {
+ //std::cout << "LfpDisplayCanvas enabling channel " << i << std::endl;
+ //lfpDisplay->enableChannel(true, i);
+ canvas->isChannelEnabled.set(i,true); //lfpDisplay->enableChannel(true, i);
+ }
+ else
+ {
+ //lfpDisplay->enableChannel(false, i);
+ canvas->isChannelEnabled.set(i,false);
+ }
+
+
+ }
+ }
+ }
+
+}
+
+
+#pragma mark - LfpTimescale -
+// -------------------------------------------------------------
+
+LfpTimescale::LfpTimescale(LfpDisplayCanvas* c, LfpDisplay* lfpDisplay)
+ : canvas(c)
+ , lfpDisplay(lfpDisplay)
+{
+
+ font = Font("Default", 16, Font::plain);
+}
+
+LfpTimescale::~LfpTimescale()
+{
+
+}
+
+void LfpTimescale::paint(Graphics& g)
+{
+
+ g.setFont(font);
+
+ g.setColour(Colour(100,100,100));
+
+ const String timeScaleUnitLabel = (timebase >= 2)?("s:"):("ms:");
+ g.drawText(timeScaleUnitLabel,5,0,100,getHeight(),Justification::left, false);
+
+ const int steps = labels.size() + 1;
+ for (int i = 0; i < steps; i++)
+ {
+
+ // TODO: (kelly) added an extra spatial dimension to the timeline ticks, may be overkill
+ if (i == 0)
+ {
+ g.drawLine(1,
+ 0,
+ 1,
+ getHeight(),
+ 3.0f);
+ }
+ if (i != 0 && i % 4 == 0)
+ {
+ g.drawLine(getWidth()/steps*i,
+ 0,
+ getWidth()/steps*i,
+ getHeight(),
+ 3.0f);
+ }
+ else if (i != 0 && i % 2 == 0)
+ {
+ g.drawLine(getWidth()/steps*i,
+ getHeight(),
+ getWidth()/steps*i,
+ getHeight() / 2,
+ 3.0f);
+ }
+ else
+ {
+ g.drawLine(getWidth()/steps*i,
+ getHeight(),
+ getWidth()/steps*i,
+ 3 * getHeight()/4,
+ 2.0f);
+ }
+
+
+ if (i != 0 && i % 2 == 0)
+ g.drawText(labels[i-1],getWidth()/steps*i+3,0,100,getHeight(),Justification::left, false);
+
+ }
+}
+
+void LfpTimescale::mouseUp(const MouseEvent &e)
+{
+ if (e.mods.isLeftButtonDown())
+ {
+ lfpDisplay->trackZoomInfo.isScrollingX = false;
+ }
+}
+
+void LfpTimescale::resized()
+{
+ setTimebase(timebase);
+}
+
+void LfpTimescale::mouseDrag(const juce::MouseEvent &e)
+{
+ if (e.mods.isLeftButtonDown()) // double check that we initiate only for left click and hold
+ {
+ if (e.mods.isCommandDown()) // CTRL + drag -> change channel spacing
+ {
+ // init state in our track zooming info struct
+ if (!lfpDisplay->trackZoomInfo.isScrollingX)
+ {
+ lfpDisplay->trackZoomInfo.isScrollingX = true;
+ lfpDisplay->trackZoomInfo.timescaleStartScale = timebase;
+ }
+
+ float timescale = lfpDisplay->trackZoomInfo.timescaleStartScale;
+ float dTimescale=0;
+ int dragDeltaX = (e.getScreenPosition().getX() - e.getMouseDownScreenX()); // invert so drag up -> scale up
+
+// std::cout << dragDeltaX << std::endl;
+ if (dragDeltaX > 0)
+ {
+ dTimescale = 0.01 * dragDeltaX;
+ }
+ else
+ {
+ // TODO: (kelly) change this to scale appropriately for -dragDeltaX
+ if (timescale > 0.25)
+ dTimescale = 0.01 * dragDeltaX;
+ }
+
+ if (timescale >= 1) // accelerate scrolling for large ranges
+ dTimescale *= 4;
+
+ if (timescale >= 5)
+ dTimescale *= 4;
+
+ if (timescale >= 10)
+ dTimescale *= 4;
+
+ // round dTimescale to the nearest 0.005 sec
+ dTimescale = ((dTimescale + (0.005/2)) / 0.005) * 0.005;
+
+ float newTimescale = timescale+dTimescale;
+
+ if (newTimescale < 0.25) newTimescale = 0.250;
+ if (newTimescale > 20) newTimescale = 20;
+
+ // don't bother updating if the new timebase is the same as the old (if clipped, for example)
+ if (timescale != newTimescale)
+ {
+ lfpDisplay->options->setTimebaseAndSelectionText(newTimescale);
+ setTimebase(canvas->timebase);
+ }
+ }
+ }
+}
+
+void LfpTimescale::setTimebase(float t)
+{
+ timebase = t;
+
+ labels.clear();
+
+ const int minWidth = 60;
+ labelIncrement = 0.025f;
+
+
+ while (getWidth() != 0 && // setTimebase can be called before LfpTimescale has width
+ getWidth() / (timebase / labelIncrement) < minWidth) // so, if width is 0 then don't iterate for scale factor
+ {
+// std::cout << getWidth() / (timebase / labelIncrement) << " is smaller than minimum width, calculating new step size" << std::endl;
+ if (labelIncrement < 0.2)
+ labelIncrement *= 2;
+ else
+ labelIncrement += 0.2f;
+ }
+
+ for (float i = labelIncrement; i < timebase; i += labelIncrement)
+ {
+ String labelString = String(i * ((timebase >= 2)?(1):(1000.0f)));
+ labels.add(labelString.substring(0,6));
+ }
+
+ repaint();
+
+}
+
+
+
+#pragma mark - LfpDisplay -
+// ---------------------------------------------------------------
+
+LfpDisplay::LfpDisplay(LfpDisplayCanvas* c, Viewport* v)
+ : singleChan(-1)
+ , canvas(c)
+ , viewport(v)
+ , channelsReversed(false)
+ , displaySkipAmt(0)
+ , m_SpikeRasterPlottingFlag(false)
+{
+ perPixelPlotter = new PerPixelBitmapPlotter(this);
+ supersampledPlotter = new SupersampledBitmapPlotter(this);
+
+// colorScheme = new LfpDefaultColourScheme();
+ colourSchemeList.add(new LfpDefaultColourScheme(this, canvas));
+ colourSchemeList.add(new LfpMonochromaticColourScheme(this, canvas));
+ colourSchemeList.add(new LfpGradientColourScheme(this, canvas));
+
+ activeColourScheme = 0;
+
+
+ plotter = perPixelPlotter;
+ m_MedianOffsetPlottingFlag = false;
+
+ totalHeight = 0;
+ colorGrouping=1;
+
+ range[0] = 1000;
+ range[1] = 500;
+ range[2] = 500000;
+
+ addMouseListener(this, true);
+
+ // hue cycle
+ //for (int i = 0; i < 15; i++)
+ //{
+ // channelColours.add(Colour(float(sin((3.14/2)*(float(i)/15))),float(1.0),float(1),float(1.0)));
+ //}
+
+// setBufferedToImage(true); // TODO: (kelly) test
+
+ backgroundColour = Colour(0,18,43);
+
+ //hand-built palette
+ channelColours.add(Colour(224,185,36));
+ channelColours.add(Colour(214,210,182));
+ channelColours.add(Colour(243,119,33));
+ channelColours.add(Colour(186,157,168));
+ channelColours.add(Colour(237,37,36));
+ channelColours.add(Colour(179,122,79));
+ channelColours.add(Colour(217,46,171));
+ channelColours.add(Colour(217, 139,196));
+ channelColours.add(Colour(101,31,255));
+ channelColours.add(Colour(141,111,181));
+ channelColours.add(Colour(48,117,255));
+ channelColours.add(Colour(184,198,224));
+ channelColours.add(Colour(116,227,156));
+ channelColours.add(Colour(150,158,155));
+ channelColours.add(Colour(82,173,0));
+ channelColours.add(Colour(125,99,32));
+
+ isPaused=false;
+
+}
+
+LfpDisplay::~LfpDisplay()
+{
+// deleteAllChildren();
+}
+
+
+
+int LfpDisplay::getNumChannels()
+{
+ return numChans;
+}
+
+
+
+int LfpDisplay::getColorGrouping()
+{
+ return colorGrouping;
+}
+
+void LfpDisplay::setColorGrouping(int i)
+{
+ colorGrouping=i;
+ getColourSchemePtr()->setColourGrouping(i);
+ setColors(); // so that channel colors get re-assigned
+
+}
+
+LfpChannelColourScheme * LfpDisplay::getColourSchemePtr()
+{
+ return colourSchemeList[activeColourScheme];
+}
+
+void LfpDisplay::setNumChannels(int numChannels)
+{
+ numChans = numChannels;
+
+
+// deleteAllChildren();
+ removeAllChildren();
+
+ channels.clear();
+ channelInfo.clear();
+ drawableChannels.clear();
+
+ totalHeight = 0;
+ cachedDisplayChannelHeight = canvas->getChannelHeight();
+
+ if (numChans > 0)
+ {
+ for (int i = 0; i < numChans; i++)
+ {
+ //std::cout << "Adding new display for channel " << i << std::endl;
+
+ LfpChannelDisplay* lfpChan = new LfpChannelDisplay(canvas, this, options, i);
+
+ //lfpChan->setColour(channelColours[i % channelColours.size()]);
+ lfpChan->setRange(range[options->getChannelType(i)]);
+ lfpChan->setChannelHeight(canvas->getChannelHeight());
+
+ addAndMakeVisible(lfpChan);
+
+ channels.add(lfpChan);
+
+ LfpChannelDisplayInfo* lfpInfo = new LfpChannelDisplayInfo(canvas, this, options, i);
+
+ //lfpInfo->setColour(channelColours[i % channelColours.size()]);
+ lfpInfo->setRange(range[options->getChannelType(i)]);
+ lfpInfo->setChannelHeight(canvas->getChannelHeight());
+ lfpInfo->setSubprocessorIdx(canvas->getChannelSubprocessorIdx(i));
+
+ addAndMakeVisible(lfpInfo);
+
+ channelInfo.add(lfpInfo);
+
+ drawableChannels.add(LfpChannelTrack{
+ lfpChan,
+ lfpInfo
+ });
+
+ savedChannelState.add(true);
+
+ totalHeight += lfpChan->getChannelHeight();
+
+ }
+
+ }
+
+ setColors();
+
+
+ std::cout << "TOTAL HEIGHT = " << totalHeight << std::endl;
+
+}
+
+void LfpDisplay::setColors()
+{
+ for (int i = 0; i < drawableChannels.size(); i++)
+ {
+
+// channels[i]->setColour(channelColours[(int(i/colorGrouping)+1) % channelColours.size()]);
+// channelInfo[i]->setColour(channelColours[(int(i/colorGrouping)+1) % channelColours.size()]);
+ drawableChannels[i].channel->setColour(getColourSchemePtr()->getColourForIndex(i));
+ drawableChannels[i].channelInfo->setColour(getColourSchemePtr()->getColourForIndex(i));
+ }
+
+}
+
+void LfpDisplay::setActiveColourSchemeIdx(int index)
+{
+ activeColourScheme = index;
+}
+
+int LfpDisplay::getActiveColourSchemeIdx()
+{
+ return activeColourScheme;
+}
+
+int LfpDisplay::getNumColourSchemes()
+{
+ return colourSchemeList.size();
+}
+
+StringArray LfpDisplay::getColourSchemeNameArray()
+{
+ StringArray nameList;
+ for (auto scheme : colourSchemeList)
+ nameList.add(scheme->getName());
+
+ return nameList;
+}
+
+int LfpDisplay::getTotalHeight()
+{
+ return totalHeight;
+}
+
+void LfpDisplay::resized()
+{
+ int totalHeight = 0;
+
+ for (int i = 0; i < drawableChannels.size(); i++)
+ {
+
+ LfpChannelDisplay* disp = drawableChannels[i].channel;
+
+ if (disp->getHidden()) continue;
+
+ disp->setBounds(canvas->leftmargin,
+ totalHeight-(disp->getChannelOverlap()*canvas->channelOverlapFactor)/2,
+ getWidth(),
+ disp->getChannelHeight()+(disp->getChannelOverlap()*canvas->channelOverlapFactor));
+
+ disp-> resized();
+
+ LfpChannelDisplayInfo* info = drawableChannels[i].channelInfo;
+
+ info->setBounds(0,
+ totalHeight-disp->getChannelHeight() + (disp->getChannelOverlap()*canvas->channelOverlapFactor)/4.0,
+ canvas->leftmargin + 50,
+ disp->getChannelHeight());
+
+ totalHeight += disp->getChannelHeight();
+
+ }
+
+ canvas->fullredraw = true; //issue full redraw
+ if (singleChan != -1)
+ viewport->setViewPosition(Point(0,singleChan*getChannelHeight()));
+
+
+
+ lfpChannelBitmap = Image(Image::ARGB, getWidth(), getHeight(), false);
+
+ //inititalize black background
+ Graphics gLfpChannelBitmap(lfpChannelBitmap);
+ gLfpChannelBitmap.setColour(Colour(0,0,0)); //background color
+ gLfpChannelBitmap.fillRect(0,0, getWidth(), getHeight());
+
+
+ canvas->fullredraw = true;
+
+ refresh();
+ // std::cout << "Total height: " << totalHeight << std::endl;
+
+}
+
+void LfpDisplay::paint(Graphics& g)
+{
+
+ g.drawImageAt(lfpChannelBitmap, canvas->leftmargin,0);
+
+}
+
+
+void LfpDisplay::refresh()
+{
+ // Ensure the lfpChannelBitmap has been initialized
+ if (lfpChannelBitmap.isNull())
+ {
+ resized();
+ }
+
+ // X-bounds of this update
+ int fillfrom = canvas->lastScreenBufferIndex[0];
+ int fillto = (canvas->screenBufferIndex[0]);
+
+ if (fillfrom<0){fillfrom=0;};
+ if (fillto>lfpChannelBitmap.getWidth()){fillto=lfpChannelBitmap.getWidth();};
+
+ int topBorder = viewport->getViewPositionY();
+ int bottomBorder = viewport->getViewHeight() + topBorder;
+
+ // clear appropriate section of the bitmap --
+ // we need to do this before each channel draws its new section of data into lfpChannelBitmap
+ Graphics gLfpChannelBitmap(lfpChannelBitmap);
+ gLfpChannelBitmap.setColour(backgroundColour); //background color
+
+ if (canvas->fullredraw)
+ {
+ gLfpChannelBitmap.fillRect(0,0, getWidth(), getHeight());
+ } else {
+ gLfpChannelBitmap.setColour(backgroundColour); //background color
+
+ gLfpChannelBitmap.fillRect(fillfrom,0, (fillto-fillfrom)+1, getHeight());
+ };
+
+
+ for (int i = 0; i < numChans; i++)
+// for (int i = 0; i < drawableChannels.size(); ++i)
+ {
+
+ int componentTop = channels[i]->getY();
+ int componentBottom = channels[i]->getHeight() + componentTop;
+
+ if ((topBorder <= componentBottom && bottomBorder >= componentTop)) // only draw things that are visible
+ {
+ if (canvas->fullredraw)
+ {
+ channels[i]->fullredraw = true;
+
+ channels[i]->pxPaint();
+ channelInfo[i]->repaint();
+
+ }
+ else
+ {
+ channels[i]->pxPaint(); // draws to lfpChannelBitmap
+
+ // it's not clear why, but apparently because the pxPaint() in a child component of LfpDisplay, we also need to issue repaint() calls for each channel, even though there's nothin to repaint there. Otherwise, the repaint call in LfpDisplay::refresh(), a few lines down, lags behind the update line by ~60 px. This could ahev something to do with teh reopaint message passing in juce. In any case, this seemingly redundant repaint here seems to fix the issue.
+
+ // we redraw from 0 to +2 (px) relative to the real redraw window, the +1 draws the vertical update line
+ channels[i]->repaint(fillfrom, 0, (fillto-fillfrom)+2, channels[i]->getHeight());
+
+
+ }
+ //std::cout << i << std::endl;
+ }
+
+ }
+
+ if (fillfrom == 0 && singleChan != -1)
+ {
+ channelInfo[singleChan]->repaint();
+ }
+
+
+ if (canvas->fullredraw)
+ {
+ repaint(0,topBorder,getWidth(),bottomBorder-topBorder);
+ }else{
+ //repaint(fillfrom, topBorder, (fillto-fillfrom)+1, bottomBorder-topBorder); // doesntb seem to be needed and results in duplicate repaint calls
+ }
+
+ canvas->fullredraw = false;
+}
+
+
+
+void LfpDisplay::setRange(float r, DataChannel::DataChannelTypes type)
+{
+ range[type] = r;
+
+ if (channels.size() > 0)
+ {
+
+ for (int i = 0; i < numChans; i++)
+ {
+ if (channels[i]->getType() == type)
+ channels[i]->setRange(range[type]);
+ }
+ canvas->fullredraw = true; //issue full redraw
+ }
+}
+
+int LfpDisplay::getRange()
+{
+ return getRange(options->getSelectedType());
+}
+
+int LfpDisplay::getRange(DataChannel::DataChannelTypes type)
+{
+ for (int i=0; i < numChans; i++)
+ {
+ if (channels[i]->getType() == type)
+ return channels[i]->getRange();
+ }
+ return 0;
+}
+
+
+void LfpDisplay::setChannelHeight(int r, bool resetSingle)
+{
+ if (!getSingleChannelState()) cachedDisplayChannelHeight = r;
+
+ for (int i = 0; i < numChans; i++)
+ {
+ channels[i]->setChannelHeight(r);
+ channelInfo[i]->setChannelHeight(r);
+ }
+ if (resetSingle && singleChan != -1)
+ {
+ //std::cout << "width " << getWidth() << " numchans " << numChans << " height " << getChannelHeight() << std::endl;
+ setSize(getWidth(),drawableChannels.size()*getChannelHeight());
+ viewport->setScrollBarsShown(true,false);
+ viewport->setViewPosition(Point(0,singleChan*r));
+ singleChan = -1;
+ for (int n = 0; n < numChans; n++)
+ {
+ channelInfo[n]->setEnabledState(savedChannelState[n]);
+ }
+ }
+
+ resized();
+
+}
+
+void LfpDisplay::setInputInverted(bool isInverted)
+{
+
+ for (int i = 0; i < numChans; i++)
+ {
+ channels[i]->setInputInverted(isInverted);
+ }
+
+ resized();
+
+}
+
+void LfpDisplay::setDrawMethod(bool isDrawMethod)
+{
+ for (int i = 0; i < numChans; i++)
+ {
+ channels[i]->setDrawMethod(isDrawMethod);
+ }
+
+ if (isDrawMethod)
+ {
+ plotter = supersampledPlotter;
+ }
+ else
+ {
+ plotter = perPixelPlotter;
+ }
+
+ resized();
+
+}
+
+
+int LfpDisplay::getChannelHeight()
+{
+// return cachedDisplayChannelHeight;
+ return drawableChannels[0].channel->getChannelHeight();
+// return channels[0]->getChannelHeight();
+}
+
+void LfpDisplay::cacheNewChannelHeight(int r)
+{
+ cachedDisplayChannelHeight = r;
+}
+
+bool LfpDisplay::getChannelsReversed()
+{
+ return channelsReversed;
+}
+
+void LfpDisplay::setChannelsReversed(bool state)
+{
+ if (state == channelsReversed) return; // bail early, in case bookkeeping error
+
+ channelsReversed = state;
+
+ if (getSingleChannelState()) return; // don't reverse if single channel
+
+ // reverse channels that are currently in drawableChannels
+ for (int i = 0, j = drawableChannels.size() - 1, len = drawableChannels.size()/2;
+ i < len;
+ i++, j--)
+ {
+ // remove channel and info components from front and back
+ // moving toward middle
+ removeChildComponent(drawableChannels[i].channel);
+ removeChildComponent(drawableChannels[j].channel);
+ removeChildComponent(drawableChannels[i].channelInfo);
+ removeChildComponent(drawableChannels[j].channelInfo);
+
+ // swap front and back, moving towards middle
+ drawableChannels.swap(i, j);
+
+ // also swap coords
+ {
+ const auto channelBoundsA = drawableChannels[i].channel->getBounds();
+ const auto channelInfoBoundsA = drawableChannels[i].channelInfo->getBounds();
+
+ drawableChannels[i].channel->setBounds(drawableChannels[j].channel->getBounds());
+ drawableChannels[i].channelInfo->setBounds(drawableChannels[j].channelInfo->getBounds());
+ drawableChannels[j].channel->setBounds(channelBoundsA);
+ drawableChannels[j].channelInfo->setBounds(channelInfoBoundsA);
+ }
+ }
+
+
+ // remove middle component if odd number of channels
+ if (drawableChannels.size() % 2 != 0)
+ {
+ removeChildComponent(drawableChannels[drawableChannels.size()/2+1].channel);
+ removeChildComponent(drawableChannels[drawableChannels.size()/2+1].channelInfo);
+ }
+
+ // add the channels and channel info again
+ for (int i = 0, len = drawableChannels.size(); i < len; i++)
+ {
+
+ if (!drawableChannels[i].channel->getHidden())
+ {
+ addAndMakeVisible(drawableChannels[i].channel);
+ addAndMakeVisible(drawableChannels[i].channelInfo);
+ }
+
+ // flag this to update the waveforms
+ drawableChannels[i].channel->fullredraw = true;
+ }
+
+ // necessary to overwrite lfpChannelBitmap's display
+ refresh();
+}
+
+int LfpDisplay::getChannelDisplaySkipAmount()
+{
+ return displaySkipAmt;
+}
+
+void LfpDisplay::setChannelDisplaySkipAmount(int skipAmt)
+{
+ displaySkipAmt = skipAmt;
+
+ if (!getSingleChannelState())
+ rebuildDrawableChannelsList();
+
+ canvas->redraw();
+}
+
+bool LfpDisplay::getMedianOffsetPlotting()
+{
+ return m_MedianOffsetPlottingFlag;
+}
+
+void LfpDisplay::setMedianOffsetPlotting(bool isEnabled)
+{
+ m_MedianOffsetPlottingFlag = isEnabled;
+}
+
+bool LfpDisplay::getSpikeRasterPlotting()
+{
+ return m_SpikeRasterPlottingFlag;
+}
+
+void LfpDisplay::setSpikeRasterPlotting(bool isEnabled)
+{
+ m_SpikeRasterPlottingFlag = isEnabled;
+}
+
+float LfpDisplay::getSpikeRasterThreshold()
+{
+ return m_SpikeRasterThreshold;
+}
+
+void LfpDisplay::setSpikeRasterThreshold(float thresh)
+{
+ m_SpikeRasterThreshold = thresh;
+}
+
+void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel)
+{
+
+ //std::cout << "Mouse wheel " << e.mods.isCommandDown() << " " << wheel.deltaY << std::endl;
+ //TODO Changing ranges with the wheel is currently broken. With multiple ranges, most
+ //of the wheel range code needs updating
+
+
+ if (e.mods.isCommandDown() && singleChan == -1) // CTRL + scroll wheel -> change channel spacing
+ {
+ int h = getChannelHeight();
+ int hdiff=0;
+
+ // std::cout << wheel.deltaY << std::endl;
+
+ if (wheel.deltaY > 0)
+ {
+ hdiff = 2;
+ }
+ else
+ {
+ if (h > 5)
+ hdiff = -2;
+ }
+
+ if (abs(h) > 100) // accelerate scrolling for large ranges
+ hdiff *= 3;
+
+ int newHeight = h+hdiff;
+
+ // constrain the spread resizing to max and min values;
+ if (newHeight < trackZoomInfo.minZoomHeight)
+ {
+ newHeight = trackZoomInfo.minZoomHeight;
+ hdiff = 0;
+ }
+ else if (newHeight > trackZoomInfo.maxZoomHeight)
+ {
+ newHeight = trackZoomInfo.maxZoomHeight;
+ hdiff = 0;
+ }
+
+ setChannelHeight(newHeight);
+ int oldX=viewport->getViewPositionX();
+ int oldY=viewport->getViewPositionY();
+
+ setBounds(0,0,getWidth()-0, getChannelHeight()*drawableChannels.size()); // update height so that the scrollbar is correct
+
+ int mouseY=e.getMouseDownY(); // should be y pos relative to inner viewport (0,0)
+ int scrollBy = (mouseY/h)*hdiff*2;// compensate for motion of point under current mouse position
+ viewport->setViewPosition(oldX,oldY+scrollBy); // set back to previous position plus offset
+
+ options->setSpreadSelection(newHeight); // update combobox
+
+ canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw
+ }
+ else
+ {
+ if (e.mods.isAltDown()) // ALT + scroll wheel -> change channel range (was SHIFT but that clamps wheel.deltaY to 0 on OSX for some reason..)
+ {
+ int h = getRange();
+
+
+ int step = options->getRangeStep(options->getSelectedType());
+
+ // std::cout << wheel.deltaY << std::endl;
+
+ if (wheel.deltaY > 0)
+ {
+ setRange(h+step,options->getSelectedType());
+ }
+ else
+ {
+ if (h > step+1)
+ setRange(h-step,options->getSelectedType());
+ }
+
+ options->setRangeSelection(h); // update combobox
+ canvas->fullredraw = true; //issue full redraw - scrolling without modifier doesnt require a full redraw
+
+ }
+ else // just scroll
+ {
+ // passes the event up to the viewport so the screen scrolls
+ if (viewport != nullptr && e.eventComponent == this) // passes only if it's not a listening event
+ viewport->mouseWheelMove(e.getEventRelativeTo(canvas), wheel);
+
+ }
+
+
+ }
+ //refresh(); // doesn't seem to be needed now that channels daraw to bitmap
+
+}
+
+void LfpDisplay::toggleSingleChannel(int chan)
+{
+ if (!getSingleChannelState())
+ {
+
+ std::cout << "Single channel on (" << chan << ")" << std::endl;
+ singleChan = chan;
+
+ int newHeight = viewport->getHeight();
+ LfpChannelTrack lfpChannelTrack{drawableChannels[chan].channel, drawableChannels[chan].channelInfo};
+ lfpChannelTrack.channelInfo->setEnabledState(true);
+ lfpChannelTrack.channelInfo->setSingleChannelState(true);
+
+ removeAllChildren();
+
+ // disable unused channels
+ for (int i = 0; i < getNumChannels(); i++)
+ {
+ if (i != chan)
+ {
+ drawableChannels[i].channel->setEnabledState(false);
+ }
+ }
+
+ // update drawableChannels, give only the single channel to focus on
+ drawableChannels.clearQuick();
+ drawableChannels.add(lfpChannelTrack);
+
+ addAndMakeVisible(lfpChannelTrack.channel);
+ addAndMakeVisible(lfpChannelTrack.channelInfo);
+
+ // set channel height and position (so that we allocate the smallest
+ // necessary image size for drawing)
+ setChannelHeight(newHeight, false);
+
+ lfpChannelTrack.channel->setTopLeftPosition(canvas->leftmargin, 0);
+ lfpChannelTrack.channelInfo->setTopLeftPosition(0, 0);
+ setSize(getWidth(), getChannelHeight());
+
+ viewport->setViewPosition(0, 0);
+
+ }
+// else if (chan == singleChan || chan == -2)
+ else
+ {
+ std::cout << "Single channel off" << std::endl;
+ for (int n = 0; n < numChans; n++)
+ {
+ channelInfo[n]->setSingleChannelState(false);
+ }
+
+ setChannelHeight(cachedDisplayChannelHeight);
+
+ reactivateChannels();
+ rebuildDrawableChannelsList();
+ }
+}
+
+void LfpDisplay::reactivateChannels()
+{
+
+ for (int n = 0; n < channels.size(); n++)
+ setEnabledState(savedChannelState[n], n);
+
+}
+
+void LfpDisplay::rebuildDrawableChannelsList()
+{
+
+ if (displaySkipAmt != 0) removeAllChildren(); // start with clean slate
+
+ Array channelsToDraw;
+ drawableChannels = Array();
+
+ // iterate over all channels and select drawable ones
+ for (int i = 0, drawableChannelNum = 0; i < channels.size(); i++)
+ {
+// std::cout << "\tchannel " << i << " has subprocessor index of " << channelInfo[i]->getSubprocessorIdx() << std::endl;
+ // if channel[i] is not sourced from the correct subprocessor, then hide it and continue
+ //if (channelInfo[i]->getSubprocessorIdx() != getDisplayedSubprocessor())
+ //{
+ // channels[i]->setHidden(true);
+ // channelInfo[i]->setHidden(true);
+ // continue;
+ //}
+
+ //std::cout << "Checking for hidden channels" << std::endl;
+ if (displaySkipAmt == 0 || (i % displaySkipAmt == 0)) // no skips, add all channels
+ {
+ channels[i]->setHidden(false);
+ channelInfo[i]->setHidden(false);
+
+ channelInfo[i]->setDrawableChannelNumber(drawableChannelNum++);
+ channelInfo[i]->resized(); // to update the conditional drawing of enableButton and channel num
+
+ channelsToDraw.add(LfpDisplay::LfpChannelTrack{
+ channels[i],
+ channelInfo[i]
+ });
+
+ addAndMakeVisible(channels[i]);
+ addAndMakeVisible(channelInfo[i]);
+ }
+ else // skip some channels
+ {
+// if (i % (displaySkipAmt) == 0) // add these channels
+// {
+// channels[i]->setHidden(false);
+// channelInfo[i]->setHidden(false);
+//
+// channelsToDraw.add(LfpDisplay::LfpChannelTrack{
+// channels[i],
+// channelInfo[i]
+// });
+//
+// addAndMakeVisible(channels[i]);
+// addAndMakeVisible(channelInfo[i]);
+// }
+// else // but not these
+// {
+ channels[i]->setHidden(true);
+ channelInfo[i]->setHidden(true);
+
+ removeChildComponent(channels[i]);
+ removeChildComponent(channelInfo[i]);
+// }
+ }
+ }
+
+ // check if channels should be added to drawableChannels in reverse
+ if (getChannelsReversed())
+ {
+ for (int i = channelsToDraw.size() - 1; i >= 0; --i)
+ {
+ drawableChannels.add(channelsToDraw[i]);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < channelsToDraw.size(); ++i)
+ {
+ drawableChannels.add(channelsToDraw[i]);
+ }
+ }
+
+ // this guards against an exception where the editor sets the drawable samplerate
+ // before the lfpDisplay is fully initialized
+ if (getHeight() > 0 && getWidth() > 0)
+ {
+ canvas->resizeToChannels();
+ }
+
+ setColors();
+}
+
+LfpBitmapPlotter * const LfpDisplay::getPlotterPtr() const
+{
+ return plotter;
+}
+
+bool LfpDisplay::getSingleChannelState()
+{
+ //if (singleChan < 0) return false;
+ //else return true;
+ return singleChan >= 0;
+}
+
+
+void LfpDisplay::mouseDown(const MouseEvent& event)
+{
+ if (drawableChannels.isEmpty())
+ {
+ return;
+ }
+
+ //int y = event.getMouseDownY(); //relative to each channel pos
+ MouseEvent canvasevent = event.getEventRelativeTo(viewport);
+ int y = canvasevent.getMouseDownY() + viewport->getViewPositionY(); // need to account for scrolling
+ int x = canvasevent.getMouseDownX();
+
+ int dist = 0;
+ int mindist = 10000;
+ int closest = 5;
+ for (int n = 0; n < drawableChannels.size(); n++) // select closest instead of relying on eventComponent
+ {
+ drawableChannels[n].channel->deselect();
+
+ int cpos = (drawableChannels[n].channel->getY() + (drawableChannels[n].channel->getHeight()/2));
+ dist = int(abs(y - cpos));
+
+// std::cout << "Mouse down at " << y << " pos is "<< cpos << " n: " << n << " dist " << dist << std::endl;
+
+ if (dist < mindist)
+ {
+ mindist = dist-1;
+ closest = n;
+ }
+ }
+
+ drawableChannels[closest].channel->select();
+ options->setSelectedType(drawableChannels[closest].channel->getType());
+
+ if (event.mods.isRightButtonDown()) { // if right click
+ PopupMenu channelMenu = channels[closest]->getOptions();
+ const int result = channelMenu.show();
+ drawableChannels[closest].channel->changeParameter(result);
+ }
+ else // if left click
+ {
+// if (singleChan != -1)
+ if (event.getNumberOfClicks() == 2) {
+ toggleSingleChannel(closest);
+ }
+
+ if (getSingleChannelState())
+ {
+
+ // std::cout << "singleChan = " << singleChan << " " << y << " " << drawableChannels[0].channel->getHeight() << " " << getRange() << std::endl;
+ //channelInfo[singleChan]->updateXY(
+ drawableChannels[0].channelInfo->updateXY(
+ float(x)/getWidth()*canvas->timebase,
+ (-(float(y)-viewport->getViewPositionY())/viewport->getViewHeight()*float(getRange()))+float(getRange()/2)
+ );
+ }
+ }
+
+// canvas->fullredraw = true;//issue full redraw
+
+// refresh();
+
+}
+
+
+bool LfpDisplay::setEventDisplayState(int ch, bool state)
+{
+ eventDisplayEnabled[ch] = state;
+ return eventDisplayEnabled[ch];
+}
+
+
+bool LfpDisplay::getEventDisplayState(int ch)
+{
+ return eventDisplayEnabled[ch];
+}
+
+
+void LfpDisplay::setEnabledState(bool state, int chan, bool updateSaved)
+{
+ if (chan < numChans)
+ {
+ channels[chan]->setEnabledState(state);
+ channelInfo[chan]->setEnabledState(state);
+
+ if (updateSaved)
+ savedChannelState.set(chan, state);
+
+ canvas->isChannelEnabled.set(chan, state);
+ }
+}
+
+bool LfpDisplay::getEnabledState(int chan)
+{
+ if (chan < numChans)
+ {
+ return channels[chan]->getEnabledState();
+ }
+
+ return false;
+}
+
+
+
+#pragma mark - LfpChannelDisplay -
+// ------------------------------------------------------------------
+
+LfpChannelDisplay::LfpChannelDisplay(LfpDisplayCanvas* c, LfpDisplay* d, LfpDisplayOptions* o, int channelNumber)
+ : canvas(c)
+ , display(d)
+ , options(o)
+ , isSelected(false)
+ , chan(channelNumber)
+ , name("")
+ , drawableChan(channelNumber)
+ , channelOverlap(300)
+ , channelHeight(30)
+ , range(250.0f)
+ , isEnabled(true)
+ , inputInverted(false)
+ , canBeInverted(true)
+ , drawMethod(false)
+ , isHidden(false)
+{
+
+
+ name = String(channelNumber+1); // default is to make the channelNumber the name
+
+
+ channelHeightFloat = (float) channelHeight;
+
+ channelFont = Font("Default", channelHeight*0.6, Font::plain);
+
+ lineColour = Colour(255,255,255);
+
+ type = options->getChannelType(channelNumber);
+ typeStr = options->getTypeName(type);
+
+}
+
+LfpChannelDisplay::~LfpChannelDisplay()
+{
+
+}
+
+void LfpChannelDisplay::resized()
+{
+ // all of this will likely need to be moved into the sharedLfpDisplay image in the lfpDisplay, not here
+ // now that the complete height is know, the sharedLfpDisplay image that we'll draw the pixel-wise lfp plot to needs to be resized
+ //lfpChannelBitmap = Image(Image::ARGB, getWidth(), getHeight(), false);
+}
+
+
+void LfpChannelDisplay::updateType()
+{
+ type = options->getChannelType(chan);
+ typeStr = options->getTypeName(type);
+}
+
+void LfpChannelDisplay::setEnabledState(bool state)
+{
+
+ //if (state)
+ //std::cout << "Setting channel " << name << " to true." << std::endl;
+ //else
+ //std::cout << "Setting channel " << name << " to false." << std::endl;
+
+ isEnabled = state;
+
+}
+
+void LfpChannelDisplay::setHidden(bool isHidden_)
+{
+ isHidden = isHidden_;
+ isEnabled = !isHidden;
+}
+
+void LfpChannelDisplay::pxPaint()
+{
+ if (!isEnabled) return; // return early if THIS display is not enabled
+
+ Image::BitmapData bdLfpChannelBitmap(display->lfpChannelBitmap, 0,0, display->lfpChannelBitmap.getWidth(), display->lfpChannelBitmap.getHeight());
+
+ int center = getHeight()/2;
+
+ // max and min of channel in absolute px coords for event displays etc - actual data might be drawn outside of this range
+ int jfrom_wholechannel= (int) (getY()+center-channelHeight/2)+1 +0 ;
+ int jto_wholechannel= (int) (getY()+center+channelHeight/2) -0;
+
+ //int jfrom_wholechannel_almost= (int) (getY()+center-channelHeight/3)+1 +0 ; // a bit less tall, for saturation warnings
+ //int jto_wholechannel_almost= (int) (getY()+center+channelHeight/3) -0;
+
+ // max and min of channel, this is the range where actual data is drawn
+ int jfrom_wholechannel_clip= (int) (getY()+center-(channelHeight)*canvas->channelOverlapFactor)+1 ;
+ int jto_wholechannel_clip = (int) (getY()+center+(channelHeight)*canvas->channelOverlapFactor) -0;
+
+ if (jfrom_wholechannel<0) {jfrom_wholechannel=0;};
+ if (jto_wholechannel >= display->lfpChannelBitmap.getHeight()) {jto_wholechannel=display->lfpChannelBitmap.getHeight()-1;};
+
+ // draw most recent drawn sample position
+ if (canvas->screenBufferIndex[chan]+1 <= display->lfpChannelBitmap.getWidth())
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k+=2) // draw line
+ bdLfpChannelBitmap.setPixelColour(canvas->screenBufferIndex[chan]+1,k, Colours::yellow);
+
+
+ bool clipWarningHi =false; // keep track if something clipped in the display, so we can draw warnings after the data pixels are done
+ bool clipWarningLo =false;
+
+ bool saturateWarningHi =false; // similar, but for saturating the amplifier, not just the display - make this warning very visible
+ bool saturateWarningLo =false;
+
+ // pre compute some colors for later so we dont do it once per pixel.
+ Colour lineColourBright = lineColour.withMultipliedBrightness(2.0f);
+ //Colour lineColourDark = lineColour.withMultipliedSaturation(0.5f).withMultipliedBrightness(0.3f);
+ Colour lineColourDark = lineColour.withMultipliedSaturation(0.5f*canvas->histogramParameterB).withMultipliedBrightness(canvas->histogramParameterB);
+
+
+ int stepSize = 1;
+ int from = 0; // for vertical line drawing in the LFP data
+ int to = 0;
+
+ int ifrom = canvas->lastScreenBufferIndex[chan] - 1; // need to start drawing a bit before the actual redraw window for the interpolated line to join correctly
+
+ if (ifrom < 0)
+ ifrom = 0;
+
+ int ito = canvas->screenBufferIndex[chan] +0;
+
+ if (fullredraw)
+ {
+ ifrom = 0; //canvas->leftmargin;
+ ito = getWidth()-stepSize;
+ fullredraw = false;
+ }
+
+ bool drawWithOffsetCorrection = display->getMedianOffsetPlotting();
+
+ LfpBitmapPlotterInfo plotterInfo; // hold and pass plotting info for each plotting method class
+
+
+ for (int i = ifrom; i < ito ; i += stepSize) // redraw only changed portion
+ {
+ if (i < display->lfpChannelBitmap.getWidth())
+ {
+ //draw zero line
+ int m = getY()+center;
+
+ if(m > 0 && m < display->lfpChannelBitmap.getHeight())
+ {
+ if ( bdLfpChannelBitmap.getPixelColour(i,m) == display->backgroundColour ) { // make sure we're not drawing over an existing plot from another channel
+ bdLfpChannelBitmap.setPixelColour(i,m,Colour(50,50,50));
+ }
+ }
+
+ //draw range markers
+ if (isSelected)
+ {
+ int start = getY()+center -channelHeight/2;
+ int jump = channelHeight/4;
+
+ for (m = start; m <= start + jump*4; m += jump)
+ {
+ if (m > 0 && m < display->lfpChannelBitmap.getHeight())
+ {
+ if ( bdLfpChannelBitmap.getPixelColour(i,m) == display->backgroundColour ) // make sure we're not drawing over an existing plot from another channel
+ bdLfpChannelBitmap.setPixelColour(i, m, Colour(80,80,80));
+ }
+ }
+ }
+
+ // draw event markers
+ int rawEventState = canvas->getYCoord(canvas->getNumChannels(), i);// get last channel+1 in buffer (represents events)
+
+ for (int ev_ch = 0; ev_ch < 8 ; ev_ch++) // for all event channels
+ {
+ if (display->getEventDisplayState(ev_ch)) // check if plotting for this channel is enabled
+ {
+ if (rawEventState & (1 << ev_ch)) // events are representet by a bit code, so we have to extract the individual bits with a mask
+ {
+// std::cout << "Drawing event." << std::endl;
+ Colour currentcolor=display->channelColours[ev_ch*2];
+
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++) // draw line
+ bdLfpChannelBitmap.setPixelColour(i,k,bdLfpChannelBitmap.getPixelColour(i,k).interpolatedWith(currentcolor,0.3f));
+
+ }
+ }
+ }
+
+ //std::cout << "e " << canvas->getYCoord(canvas->getNumChannels()-1, i) << std::endl;
+
+
+ // set max-min range for plotting, used in all methods
+ double a = (canvas->getYCoordMax(chan, i)/range*channelHeightFloat);
+ double b = (canvas->getYCoordMin(chan, i)/range*channelHeightFloat);
+
+ double mean = (canvas->getMean(chan)/range*channelHeightFloat);
+
+ if (drawWithOffsetCorrection)
+ {
+ a -= mean;
+ b -= mean;
+ }
+
+ double a_raw = canvas->getYCoordMax(chan, i);
+ double b_raw = canvas->getYCoordMin(chan, i);
+ double from_raw=0; double to_raw=0;
+
+ //double m = (canvas->getYCoordMean(chan, i)/range*channelHeightFloat)+getHeight()/2;
+ if (achannelOverlapFactor;
+ if (lm>0)
+ lm=-lm;
+
+ if (from > -lm) {from = -lm; clipWarningHi=true;};
+ if (to > -lm) {to = -lm; clipWarningHi=true;};
+ if (from < lm) {from = lm; clipWarningLo=true;};
+ if (to < lm) {to = lm; clipWarningLo=true;};
+
+
+ // test if raw data is clipped for displaying saturation warning
+ if (from_raw > options->selectedSaturationValueFloat) { saturateWarningHi=true;};
+ if (to_raw > options->selectedSaturationValueFloat) { saturateWarningHi=true;};
+ if (from_raw < -options->selectedSaturationValueFloat) { saturateWarningLo=true;};
+ if (to_raw < -options->selectedSaturationValueFloat) { saturateWarningLo=true;};
+
+ bool spikeFlag = display->getSpikeRasterPlotting()
+ && !(saturateWarningHi || saturateWarningLo)
+ && (from_raw - canvas->getYCoordMean(chan, i) < display->getSpikeRasterThreshold()
+ || to_raw - canvas->getYCoordMean(chan, i) < display->getSpikeRasterThreshold());
+
+ from = from + getHeight()/2; // so the plot is centered in the channeldisplay
+ to = to + getHeight()/2;
+
+ int samplerange = to - from;
+
+ if (drawMethod) // switched between 'supersampled' drawing and simple pixel wise drawing
+ { // histogram based supersampling method
+ plotterInfo.channelID = chan;
+ plotterInfo.samp = i;
+ plotterInfo.y = getY();
+ plotterInfo.from = from;
+ plotterInfo.height = getHeight();
+ plotterInfo.lineColourBright = lineColourBright;
+ plotterInfo.lineColourDark = lineColourDark;
+ plotterInfo.range = range;
+ plotterInfo.channelHeightFloat = channelHeightFloat;
+ plotterInfo.sampleCountPerPixel = canvas->getSampleCountPerPixel(i);
+ plotterInfo.samplesPerPixel = canvas->getSamplesPerPixel(chan, i);
+ plotterInfo.histogramParameterA = canvas->histogramParameterA;
+ plotterInfo.samplerange = samplerange;
+
+ // TODO: (kelly) complete transition toward plotter class encapsulation
+// display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo);
+
+ }
+ else //drawmethod
+ { // simple per-pixel min-max drawing, has no anti-aliasing, but runs faster
+
+ plotterInfo.channelID = chan;
+ plotterInfo.y = getY();
+ plotterInfo.from = from;
+ plotterInfo.to = to;
+ plotterInfo.samp = i;
+ plotterInfo.lineColour = lineColour;
+
+ // TODO: (kelly) complete transition toward plotter class encapsulation
+// display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo); // plotterInfo is prepared above
+
+ }
+
+ // Do the actual plotting for the selected plotting method
+ if (!display->getSpikeRasterPlotting())
+ display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo);
+
+
+
+
+ // now draw warnings, if needed
+ if (canvas->drawClipWarning) // draw simple warning if display cuts off data
+ {
+
+ if(clipWarningHi) {
+ for (int j=0; j<=3; j++)
+ {
+ int clipmarker = jto_wholechannel_clip;
+
+ if(clipmarker>0 && clipmarkerlfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,clipmarker-j,Colour(255,255,255));
+ }
+ }
+ }
+
+ if(clipWarningLo) {
+ for (int j=0; j<=3; j++)
+ {
+ int clipmarker = jfrom_wholechannel_clip;
+
+ if(clipmarker>0 && clipmarkerlfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,clipmarker+j,Colour(255,255,255));
+ }
+ }
+ }
+
+ clipWarningHi=false;
+ clipWarningLo=false;
+ }
+
+ if (spikeFlag) // draw spikes
+ {
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++){ // draw line
+ if(k>0 && klfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,k,lineColour);
+ }
+ };
+ }
+
+
+ if (canvas->drawSaturationWarning) // draw bigger warning if actual data gets cuts off
+ {
+
+ if(saturateWarningHi || saturateWarningLo) {
+
+
+ for (int k=jfrom_wholechannel; k<=jto_wholechannel; k++){ // draw line
+ Colour thiscolour=Colour(255,0,0);
+ if (fmod((i+k),50)>25){
+ thiscolour=Colour(255,255,255);
+ }
+ if(k>0 && klfpChannelBitmap.getHeight()){
+ bdLfpChannelBitmap.setPixelColour(i,k,thiscolour);
+ }
+ };
+ }
+
+ saturateWarningHi=false; // we likely just need one of this because for this warning we dont care if its saturating on the positive or negative side
+ saturateWarningLo=false;
+ }
+ } // if i < getWidth()
+
+ } // for i (x pixels)
+
+}
+
+void LfpChannelDisplay::paint(Graphics& g) {}
+
+
+
+PopupMenu LfpChannelDisplay::getOptions()
+{
+
+ PopupMenu menu;
+ menu.addItem(1, "Invert signal", true, inputInverted);
+
+ return menu;
+}
+
+void LfpChannelDisplay::changeParameter(int id)
+{
+ switch (id)
+ {
+ case 1:
+ setInputInverted(!inputInverted);
+ default:
+ break;
+ }
+}
+
+void LfpChannelDisplay::setRange(float r)
+{
+
+ range = r;
+
+ //std::cout << "Range: " << r << std::endl;
+}
+
+int LfpChannelDisplay::getRange()
+{
+ return range;
+}
+
+
+void LfpChannelDisplay::select()
+{
+ isSelected = true;
+}
+
+void LfpChannelDisplay::deselect()
+{
+ isSelected = false;
+}
+
+bool LfpChannelDisplay::getSelected()
+{
+ return isSelected;
+}
+
+void LfpChannelDisplay::setColour(Colour c)
+{
+ lineColour = c;
+}
+
+
+void LfpChannelDisplay::setChannelHeight(int c)
+{
+ channelHeight = c;
+
+ channelHeightFloat = (float) channelHeight;
+
+ if (!inputInverted)
+ channelHeightFloat = -channelHeightFloat;
+
+ channelOverlap = channelHeight*2;
+}
+
+int LfpChannelDisplay::getChannelHeight()
+{
+
+ return channelHeight;
+}
+
+void LfpChannelDisplay::setChannelOverlap(int overlap)
+{
+ channelOverlap = overlap;
+}
+
+
+int LfpChannelDisplay::getChannelOverlap()
+{
+ return channelOverlap;
+}
+
+int LfpChannelDisplay::getChannelNumber()
+{
+ return chan;
+}
+
+String LfpChannelDisplay::getName()
+{
+ return name;
+}
+
+int LfpChannelDisplay::getDrawableChannelNumber()
+{
+ return drawableChan;
+}
+
+void LfpChannelDisplay::setDrawableChannelNumber(int channelId)
+{
+ drawableChan = channelId;
+}
+
+void LfpChannelDisplay::setCanBeInverted(bool _canBeInverted)
+{
+ canBeInverted = _canBeInverted;
+}
+
+void LfpChannelDisplay::setInputInverted(bool isInverted)
+{
+ if (canBeInverted)
+ {
+ inputInverted = isInverted;
+ setChannelHeight(channelHeight);
+ }
+}
+
+void LfpChannelDisplay::setDrawMethod(bool isDrawMethod)
+{
+
+ drawMethod = isDrawMethod;
+
+}
+
+
+void LfpChannelDisplay::setName(String name_)
+{
+ name = name_;
+}
+
+DataChannel::DataChannelTypes LfpChannelDisplay::getType()
+{
+ return type;
+}
+
+
+
+#pragma mark - LfpChannelDisplayInfo -
+// -------------------------------
+
+LfpChannelDisplayInfo::LfpChannelDisplayInfo(LfpDisplayCanvas* canvas_, LfpDisplay* display_, LfpDisplayOptions* options_, int ch)
+ : LfpChannelDisplay(canvas_, display_, options_, ch)
+{
+
+ chan = ch;
+ x = -1.0f;
+ y = -1.0f;
+
+// enableButton = new UtilityButton(String(ch+1), Font("Small Text", 13, Font::plain));
+ enableButton = new UtilityButton("", Font("Small Text", 13, Font::plain));
+ enableButton->setRadius(5.0f);
+
+ enableButton->setEnabledState(true);
+ enableButton->setCorners(true, true, true, true);
+ enableButton->addListener(this);
+ enableButton->setClickingTogglesState(true);
+ enableButton->setToggleState(true, dontSendNotification);
+
+ isSingleChannel = false;
+
+ addAndMakeVisible(enableButton);
+
+}
+
+void LfpChannelDisplayInfo::updateType()
+{
+ type = options->getChannelType(chan);
+ typeStr = options->getTypeName(type);
+ repaint();
+}
+
+void LfpChannelDisplayInfo::buttonClicked(Button* button)
+{
+
+ bool state = button->getToggleState();
+
+ display->setEnabledState(state, chan);
+
+ //UtilityButton* b = (UtilityButton*) button;
+
+ // if (state)
+ // {
+ // b->setLabel("ON");
+ // } else {
+ // b->setLabel("OFF");
+ // }
+
+ //std::cout << "Turn channel " << chan << " to " << button->getToggleState() << std::endl;
+
+}
+
+void LfpChannelDisplayInfo::setEnabledState(bool state)
+{
+ enableButton->setToggleState(state, sendNotification);
+}
+
+void LfpChannelDisplayInfo::setSingleChannelState(bool state)
+{
+ isSingleChannel = state;
+}
+
+int LfpChannelDisplayInfo::getChannelSampleRate()
+{
+ return samplerate;
+}
+
+void LfpChannelDisplayInfo::setChannelSampleRate(int samplerate_)
+{
+ samplerate = samplerate_;
+}
+
+void LfpChannelDisplayInfo::mouseDrag(const MouseEvent &e)
+{
+ if (e.mods.isLeftButtonDown()) // double check that we initiate only for left click and hold
+ {
+ if (e.mods.isCommandDown() && !display->getSingleChannelState()) // CTRL + drag -> change channel spacing
+ {
+
+ // init state in our track zooming info struct
+ if (!display->trackZoomInfo.isScrollingY)
+ {
+ auto & zoomInfo = display->trackZoomInfo;
+
+ zoomInfo.isScrollingY = true;
+ zoomInfo.componentStartHeight = getChannelHeight();
+ zoomInfo.zoomPivotRatioY = (getY() + e.getMouseDownY())/(float)display->getHeight();
+ zoomInfo.zoomPivotRatioX = (getX() + e.getMouseDownX())/(float)display->getWidth();
+ zoomInfo.zoomPivotViewportOffset = getPosition() + e.getMouseDownPosition() - canvas->viewport->getViewPosition();
+
+ zoomInfo.unpauseOnScrollEnd = !display->isPaused;
+ if (!display->isPaused) display->options->togglePauseButton(true);
+ }
+
+ int h = display->trackZoomInfo.componentStartHeight;
+ int hdiff=0;
+ int dragDeltaY = -0.1 * (e.getScreenPosition().getY() - e.getMouseDownScreenY()); // invert so drag up -> scale up
+
+// std::cout << dragDeltaY << std::endl;
+ if (dragDeltaY > 0)
+ {
+ hdiff = 2 * dragDeltaY;
+ }
+ else
+ {
+ if (h > 5)
+ hdiff = 2 * dragDeltaY;
+ }
+
+ if (abs(h) > 100) // accelerate scrolling for large ranges
+ hdiff *= 3;
+
+ int newHeight = h+hdiff;
+
+ // constrain the spread resizing to max and min values;
+ if (newHeight < display->trackZoomInfo.minZoomHeight)
+ {
+ newHeight = display->trackZoomInfo.minZoomHeight;
+ }
+ else if (newHeight > display->trackZoomInfo.maxZoomHeight)
+ {
+ newHeight = display->trackZoomInfo.maxZoomHeight;
+ }
+
+ // return early if there is nothing to update
+ if (newHeight == getChannelHeight())
+ {
+ return;
+ }
+
+ // set channel heights for all channel
+// display->setChannelHeight(newHeight);
+ for (int i = 0; i < display->getNumChannels(); ++i)
+ {
+ display->channels[i]->setChannelHeight(newHeight);
+ display->channelInfo[i]->setChannelHeight(newHeight);
+ }
+
+ options->setSpreadSelection(newHeight, false, true); // update combobox
+
+ canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw
+
+ display->setBounds(0,0,display->getWidth()-0, display->getChannelHeight()*display->drawableChannels.size()); // update height so that the scrollbar is correct
+
+ int newViewportY = display->trackZoomInfo.zoomPivotRatioY * display->getHeight() - display->trackZoomInfo.zoomPivotViewportOffset.getY();
+ if (newViewportY < 0) newViewportY = 0; // make sure we don't adjust beyond the edge of the actual view
+
+ canvas->viewport->setViewPosition(0, newViewportY);
+ }
+ }
+}
+
+void LfpChannelDisplayInfo::mouseUp(const MouseEvent &e)
+{
+ if (e.mods.isLeftButtonDown() && display->trackZoomInfo.isScrollingY)
+ {
+ display->trackZoomInfo.isScrollingY = false;
+ if (display->trackZoomInfo.unpauseOnScrollEnd)
+ {
+ display->isPaused = false;
+ display->options->togglePauseButton(false);
+ }
+ }
+}
+
+void LfpChannelDisplayInfo::paint(Graphics& g)
+{
+
+ int center = getHeight()/2 - (isSingleChannel?(75):(0));
+ const bool showChannelNumbers = options->getChannelNameState();
+
+ // Draw the channel numbers
+ g.setColour(Colours::grey);
+ const String channelString = (isChannelNumberHidden() ? ("--") :
+ showChannelNumbers ? String(getChannelNumber() + 1) : getName());
+ bool isCentered = !getEnabledButtonVisibility();
+
+ g.drawText(channelString,
+ showChannelNumbers ? 6 : 2,
+ center-4,
+ getWidth()/2,
+ 10,
+ isCentered ? Justification::centred : Justification::centredLeft,
+ false);
+
+ g.setColour(lineColour);
+ g.fillRect(0, 0, 2, getHeight());
+
+ if (getChannelTypeStringVisibility())
+ {
+ g.setFont(Font("Small Text", 13, Font::plain));
+ g.drawText(typeStr,5,center+10,41,10,Justification::centred,false);
+ }
+ // g.setFont(channelHeightFloat*0.3);
+ g.setFont(Font("Small Text", 11, Font::plain));
+
+ if (isSingleChannel)
+ {
+ g.setColour(Colours::darkgrey);
+ g.drawText("STD:", 5, center+90,41,10,Justification::centred,false);
+ g.drawText("MEAN:", 5, center+40,41,10,Justification::centred,false);
+
+ if (x > 0)
+ {
+ g.drawText("uV:", 5, center+140,41,10,Justification::centred,false);
+ }
+ //g.drawText("Y:", 5, center+200,41,10,Justification::centred,false);
+
+ g.setColour(Colours::grey);
+ g.drawText(String(canvas->getStd(chan)), 5, center+110,41,10,Justification::centred,false);
+ g.drawText(String(canvas->getMean(chan)), 5, center+60,41,10,Justification::centred,false);
+ if (x > 0)
+ {
+ //g.drawText(String(x), 5, center+150,41,10,Justification::centred,false);
+ g.drawText(String(y), 5, center+160,41,10,Justification::centred,false);
+ }
+
+ }
+
+ // g.drawText(name, 10, center-channelHeight/2, 200, channelHeight, Justification::left, false);
+
+}
+
+void LfpChannelDisplayInfo::updateXY(float x_, float y_)
+{
+ x = x_;
+ y = y_;
+}
+
+void LfpChannelDisplayInfo::resized()
+{
+
+ int center = getHeight()/2 - (isSingleChannel?(75):(0));
+ setEnabledButtonVisibility(getHeight() >= 16);
+
+ if (getEnabledButtonVisibility())
+ {
+ enableButton->setBounds(getWidth()/2 - 10, center - 5, 10, 10);
+ }
+
+ setChannelNumberIsHidden(getHeight() < 16 && (getDrawableChannelNumber() + 1) % 10 != 0);
+
+ setChannelTypeStringVisibility(getHeight() > 34);
+}
+
+void LfpChannelDisplayInfo::setEnabledButtonVisibility(bool shouldBeVisible)
+{
+ if (shouldBeVisible)
+ {
+ addAndMakeVisible(enableButton);
+ }
+ else if (enableButton->isVisible())
+ {
+ removeChildComponent(enableButton);
+ enableButton->setVisible(false);
+ }
+
+}
+
+bool LfpChannelDisplayInfo::getEnabledButtonVisibility()
+{
+ return enableButton->isVisible();
+}
+
+void LfpChannelDisplayInfo::setChannelTypeStringVisibility(bool shouldBeVisible)
+{
+ channelTypeStringIsVisible = shouldBeVisible;
+}
+
+bool LfpChannelDisplayInfo::getChannelTypeStringVisibility()
+{
+ return channelTypeStringIsVisible || isSingleChannel;
+}
+
+void LfpChannelDisplayInfo::setChannelNumberIsHidden(bool shouldBeHidden)
+{
+ channelNumberHidden = shouldBeHidden;
+}
+
+bool LfpChannelDisplayInfo::isChannelNumberHidden()
+{
+ return channelNumberHidden;
+}
+
+
+
+
+#pragma mark - EventDisplayInterface -
+// Event display Options --------------------------------------------------------------------
+
+EventDisplayInterface::EventDisplayInterface(LfpDisplay* display_, LfpDisplayCanvas* canvas_, int chNum):
+ isEnabled(true), display(display_), canvas(canvas_)
+{
+
+ channelNumber = chNum;
+
+ chButton = new UtilityButton(String(channelNumber+1), Font("Small Text", 13, Font::plain));
+ chButton->setRadius(5.0f);
+ chButton->setBounds(4,4,14,14);
+ chButton->setEnabledState(true);
+ chButton->setCorners(true, false, true, false);
+ //chButton.color = display->channelColours[channelNumber*2];
+ chButton->addListener(this);
+ addAndMakeVisible(chButton);
+
+
+ checkEnabledState();
+
+}
+
+EventDisplayInterface::~EventDisplayInterface()
+{
+
+}
+
+void EventDisplayInterface::checkEnabledState()
+{
+ isEnabled = display->getEventDisplayState(channelNumber);
+
+ //repaint();
+}
+
+void EventDisplayInterface::buttonClicked(Button* button)
+{
+ checkEnabledState();
+ if (isEnabled)
+ {
+ display->setEventDisplayState(channelNumber, false);
+ }
+ else
+ {
+ display->setEventDisplayState(channelNumber, true);
+ }
+
+ repaint();
+
+}
+
+
+void EventDisplayInterface::paint(Graphics& g)
+{
+
+ checkEnabledState();
+
+ if (isEnabled)
+ {
+ g.setColour(display->channelColours[channelNumber*2]);
+ g.fillRoundedRectangle(2,2,18,18,6.0f);
+ }
+
+
+ //g.drawText(String(channelNumber), 8, 2, 200, 15, Justification::left, false);
+
+}
+
+
+
+#pragma mark - LfpViewport -
+// Lfp Viewport -------------------------------------------
+
+LfpViewport::LfpViewport(LfpDisplayCanvas *canvas)
+ : Viewport()
+{
+ this->canvas = canvas;
+}
+
+void LfpViewport::visibleAreaChanged(const Rectangle& newVisibleArea)
+{
+ canvas->fullredraw = true;
+ canvas->refresh();
+}
+
+
+
+#pragma mark - PerPixelBitmapPlotter -
+
+PerPixelBitmapPlotter::PerPixelBitmapPlotter(LfpDisplay * lfpDisplay)
+ : LfpBitmapPlotter(lfpDisplay)
+{ }
+
+void PerPixelBitmapPlotter::plot(Image::BitmapData &bitmapData, LfpBitmapPlotterInfo &pInfo)
+{
+ int jfrom = pInfo.from + pInfo.y;
+ int jto = pInfo.to + pInfo.y;
+
+ //if (yofs<0) {yofs=0;};
+
+ if (pInfo.samp < 0) {pInfo.samp = 0;};
+ if (pInfo.samp >= display->lfpChannelBitmap.getWidth()) {pInfo.samp = display->lfpChannelBitmap.getWidth()-1;}; // this shouldnt happen, there must be some bug above - to replicate, run at max refresh rate where draws overlap the right margin by a lot
+
+ if (jfrom<0) {jfrom=0;};
+ if (jto >= display->lfpChannelBitmap.getHeight()) {jto=display->lfpChannelBitmap.getHeight()-1;};
+
+
+ for (int j = jfrom; j <= jto; j += 1)
+ {
+
+ //uint8* const pu8Pixel = bdSharedLfpDisplay.getPixelPointer( (int)(i),(int)(j));
+ //*(pu8Pixel) = 200;
+ //*(pu8Pixel+1) = 200;
+ //*(pu8Pixel+2) = 200;
+
+ bitmapData.setPixelColour(pInfo.samp,j,pInfo.lineColour);
+
+ }
+}
+
+
+
+#pragma mark - LfpSupersampledBitmapPlotter -
+
+SupersampledBitmapPlotter::SupersampledBitmapPlotter(LfpDisplay * lfpDisplay)
+ : LfpBitmapPlotter(lfpDisplay)
+{ }
+
+void SupersampledBitmapPlotter::plot(Image::BitmapData &bdLfpChannelBitmap, LfpBitmapPlotterInfo &pInfo)
+{
+ std::array samplesThisPixel = pInfo.samplesPerPixel;
+// int sampleCountThisPixel = lfpDisplay->canvas->getSampleCountPerPixel(pInfo.samp);
+ int sampleCountThisPixel = pInfo.sampleCountPerPixel;
+
+ if (pInfo.samplerange>0 && sampleCountThisPixel>0)
+ {
+
+ //float localHist[samplerange]; // simple histogram
+ Array rangeHist; // [samplerange]; // paired range histogram, same as plotting at higher res. and subsampling
+
+ for (int k = 0; k <= pInfo.samplerange; k++)
+ rangeHist.add(0);
+
+ for (int k = 0; k <= sampleCountThisPixel; k++) // add up paired-range histogram per pixel - for each pair fill intermediate with uniform distr.
+ {
+ int cs_this = (((samplesThisPixel[k]/pInfo.range*pInfo.channelHeightFloat)+pInfo.height/2)-pInfo.from); // sample values -> pixel coordinates relative to from
+ int cs_next = (((samplesThisPixel[k+1]/pInfo.range*pInfo.channelHeightFloat)+pInfo.height/2)-pInfo.from);
+
+
+ if (cs_this<0) {cs_this=0;}; //here we could clip the diaplay to the max/min, or ignore out of bound values, not sure which one is better
+ if (cs_this>pInfo.samplerange) {cs_this=pInfo.samplerange;};
+ if (cs_next<0) {cs_next=0;};
+ if (cs_next>pInfo.samplerange) {cs_next=pInfo.samplerange;};
+
+ int hfrom=0;
+ int hto=0;
+
+ if (cs_this1.0f) {a=1.0f;};
+ if (a<0.0f) {a=0.0f;};
+
+
+ //Colour gradedColor = lineColour.withMultipliedBrightness(2.0f).interpolatedWith(lineColour.withMultipliedSaturation(0.6f).withMultipliedBrightness(0.3f),1-a) ;
+ Colour gradedColor = pInfo.lineColourBright.interpolatedWith(pInfo.lineColourDark,1-a);
+ //Colour gradedColor = Colour(0,255,0);
+
+ int ploty = pInfo.from + s + pInfo.y;
+ if(ploty>0 && ploty < display->lfpChannelBitmap.getHeight()) {
+ bdLfpChannelBitmap.setPixelColour(pInfo.samp, pInfo.from + s + pInfo.y, gradedColor);
+ }
+ }
+
+ } else {
+
+ int ploty = pInfo.from + pInfo.y;
+ if(ploty>0 && ploty < display->lfpChannelBitmap.getHeight()) {
+ bdLfpChannelBitmap.setPixelColour(pInfo.samp, ploty, pInfo.lineColour);
+ }
+ }
+}
+
+
+
+#pragma mark - LfpChannelColourScheme -
+
+int LfpChannelColourScheme::colourGrouping = 1;
+
+void LfpChannelColourScheme::setColourGrouping(int grouping)
+{
+ colourGrouping = grouping;
+}
+
+int LfpChannelColourScheme::getColourGrouping()
+{
+ return colourGrouping;
+}
+
+
+
+#pragma mark - LfpDefaultColourScheme -
+
+Array LfpDefaultColourScheme::colourList = []() -> Array {
+ Array colours;
+ colours.add(Colour(224,185,36));
+ colours.add(Colour(214,210,182));
+ colours.add(Colour(243,119,33));
+ colours.add(Colour(186,157,168));
+ colours.add(Colour(237,37,36));
+ colours.add(Colour(179,122,79));
+ colours.add(Colour(217,46,171));
+ colours.add(Colour(217, 139,196));
+ colours.add(Colour(101,31,255));
+ colours.add(Colour(141,111,181));
+ colours.add(Colour(48,117,255));
+ colours.add(Colour(184,198,224));
+ colours.add(Colour(116,227,156));
+ colours.add(Colour(150,158,155));
+ colours.add(Colour(82,173,0));
+ colours.add(Colour(125,99,32));
+ return colours;
+}();
+
+LfpDefaultColourScheme::LfpDefaultColourScheme(LfpDisplay* display, LfpDisplayCanvas* canvas)
+ : LfpViewer::LfpChannelColourScheme(LfpDefaultColourScheme::colourList.size(), display, canvas)
+{
+ setName("Default");
+}
+
+void LfpDefaultColourScheme::paint(Graphics &g)
+{
+
+}
+
+void LfpDefaultColourScheme::resized()
+{
+
+}
+
+const Colour LfpDefaultColourScheme::getColourForIndex(int index) const
+{
+// return colourList[index % colourList.size()];
+ return colourList[(int(index/colourGrouping)) % colourList.size()];
+}
+
+
+
+#pragma mark - LfpMonochromaticColorScheme
+
+LfpMonochromaticColourScheme::LfpMonochromaticColourScheme(LfpDisplay* display, LfpDisplayCanvas* canvas)
+ : LfpChannelColourScheme (8, display, canvas)
+ , isBlackAndWhite (false)
+ , colourPattern (DOWN_UP)
+{
+ setName("Monochromatic");
+
+ numChannelsLabel = new Label("numChannelsLabel", "Num Color Steps");
+ numChannelsLabel->setFont(Font("Default", 13.0f, Font::plain));
+ numChannelsLabel->setColour(Label::textColourId, Colour(100, 100, 100));
+ addAndMakeVisible(numChannelsLabel);
+
+ StringArray numChannelsSelectionOptions = {"4", "8", "16"};
+ numChannelsSelection = new ComboBox("numChannelsSelection");
+ numChannelsSelection->addItemList(numChannelsSelectionOptions, 1);
+ numChannelsSelection->setEditableText(true);
+ numChannelsSelection->addListener(this);
+ numChannelsSelection->setSelectedId(2, dontSendNotification);
+ addAndMakeVisible(numChannelsSelection);
+
+
+ baseHueLabel = new Label("baseHue", "Hue");
+ baseHueLabel->setFont(Font("Default", 13.0f, Font::plain));
+ baseHueLabel->setColour(Label::textColourId, Colour(100, 100, 100));
+ addAndMakeVisible(baseHueLabel);
+
+ baseHueSlider = new Slider;
+ baseHueSlider->setRange(0, 1);
+ baseHueSlider->setValue(0);
+ baseHueSlider->setTextBoxStyle(Slider::NoTextBox, false, 0, 0);
+ baseHueSlider->addListener(this);
+ addAndMakeVisible(baseHueSlider);
+
+ baseHueSlider->addMouseListener(this, true);
+
+
+ colourPatternLabel = new Label("colourPatternLabel", "Pattern");
+ colourPatternLabel->setFont(Font("Default", 13.0f, Font::plain));
+ colourPatternLabel->setColour(Label::textColourId, Colour(100, 100, 100));
+ addAndMakeVisible(colourPatternLabel);
+
+ StringArray colourPatternSelectionOptions = {"Down", "Up", "Down-Up", "Up-Down"};
+ colourPatternSelection = new ComboBox("colourPatternSelection");
+ colourPatternSelection->addItemList(colourPatternSelectionOptions, 1);
+ colourPatternSelection->setEditableText(false);
+ colourPatternSelection->addListener(this);
+ colourPatternSelection->setSelectedId(colourPattern + 1, dontSendNotification);
+ addAndMakeVisible(colourPatternSelection);
+
+ baseHue = Colour::fromHSV(0, 1, 1, 1);
+ swatchHue = baseHue;
+
+ calculateColourSeriesFromBaseHue();
+}
+
+void LfpMonochromaticColourScheme::paint(Graphics &g)
+{
+ g.setColour(swatchHue);
+ g.fillRect(colourSwatchRect);
+}
+
+void LfpMonochromaticColourScheme::resized()
+{
+ numChannelsLabel->setBounds(0, 5, 120, 25);
+ numChannelsSelection->setBounds(numChannelsLabel->getRight(),
+ numChannelsLabel->getY(),
+ 60,
+ 25);
+
+ baseHueLabel->setBounds(0, numChannelsLabel->getBottom(), 35, 25);
+ baseHueSlider->setBounds(baseHueLabel->getRight(),
+ baseHueLabel->getY(),
+ numChannelsSelection->getRight() - baseHueLabel->getRight() - 20,
+ 25);
+
+ colourSwatchRect.setBounds(baseHueSlider->getRight() + 5, baseHueSlider->getY() + 5, 15, baseHueSlider->getHeight() - 10);
+
+ colourPatternLabel->setBounds(0, baseHueLabel->getBottom(), 80, 25);
+ colourPatternSelection->setBounds(colourPatternLabel->getRight(),
+ colourPatternLabel->getY(),
+ numChannelsSelection->getRight() - colourPatternLabel->getRight(),
+ 25);
+
+}
+
+void LfpMonochromaticColourScheme::sliderValueChanged(Slider *sl)
+{
+ swatchHue = Colour::fromHSV(sl->getValue(), 1, 1, 1);
+ repaint(colourSwatchRect);
+}
+
+void LfpMonochromaticColourScheme::comboBoxChanged(ComboBox *cb)
+{
+ if (cb == numChannelsSelection)
+ {
+ int numChannelsColourSpread = 0;
+ if (cb->getSelectedId())
+ {
+ numChannelsColourSpread = cb->getText().getIntValue();
+ }
+ else
+ {
+ numChannelsColourSpread = cb->getText().getIntValue();
+ if (numChannelsColourSpread < 1) numChannelsColourSpread = 1;
+ else if (numChannelsColourSpread > 16) numChannelsColourSpread = 16;
+
+ cb->setText(String(numChannelsColourSpread), dontSendNotification);
+ }
+
+ setNumColourSeriesSteps(numChannelsColourSpread);
+ }
+ else if (cb == colourPatternSelection)
+ {
+ setColourPattern((ColourPattern)(cb->getSelectedId() - 1));
+ }
+ calculateColourSeriesFromBaseHue();
+ lfpDisplay->setColors();
+// canvas->fullredraw = true;
+ canvas->redraw();
+}
+
+void LfpMonochromaticColourScheme::mouseUp(const MouseEvent &e)
+{
+ if (swatchHue.getARGB() != baseHue.getARGB())
+ {
+ baseHue = swatchHue;
+ calculateColourSeriesFromBaseHue();
+ lfpDisplay->setColors();
+ canvas->redraw();
+ }
+}
+
+void LfpMonochromaticColourScheme::setBaseHue(Colour base)
+{
+ baseHue = base;
+ calculateColourSeriesFromBaseHue();
+}
+
+const Colour LfpMonochromaticColourScheme::getBaseHue() const
+{
+ return baseHue;
+}
+
+void LfpMonochromaticColourScheme::setNumColourSeriesSteps(int numSteps)
+{
+ numColourChannels = numSteps;
+}
+
+int LfpMonochromaticColourScheme::getNumColourSeriesSteps()
+{
+ return numColourChannels;
+}
+
+const Colour LfpMonochromaticColourScheme::getColourForIndex(int index) const
+{
+ int colourIdx = (int(index/colourGrouping) % numColourChannels);
+
+ // adjust for oscillating patterns
+ if (colourPattern == DOWN_UP || colourPattern == UP_DOWN)
+ {
+ int mid = numColourChannels / 2;
+ if (colourIdx > mid)
+ {
+ if (numColourChannels % 2 == 0)
+ colourIdx = numColourChannels - colourIdx;
+ else
+ colourIdx = (numColourChannels - colourIdx) * 2 - 1;
+ }
+ else if (numColourChannels % 2 != 0)
+ {
+ colourIdx *= 2;
+ }
+ }
+
+ // invert if the pattern is UP or UP_DOWN
+ if (colourPattern == UP)
+ colourIdx = (numColourChannels - 1) - colourIdx;
+ else if (colourPattern == UP_DOWN)
+ colourIdx = (colourList.size() - 1) - colourIdx;
+
+ return colourList[colourIdx];
+}
+
+void LfpMonochromaticColourScheme::calculateColourSeriesFromBaseHue()
+{
+ colourList.clear();
+
+ int coloursToCalculate = numColourChannels;
+
+ if (numColourChannels % 2 == 0 && (colourPattern == DOWN_UP || colourPattern == UP_DOWN))
+ {
+ coloursToCalculate = coloursToCalculate / 2 + 1;
+ }
+
+ for (int i = 0; i < coloursToCalculate; ++i)
+ {
+ float saturation = 1 - (i / float(coloursToCalculate + 1));
+ colourList.add(baseHue.withMultipliedSaturation(saturation));
+ }
+}
+
+
+
+#pragma mark - LfpGradientColourScheme
+
+LfpGradientColourScheme::LfpGradientColourScheme(LfpDisplay * display, LfpDisplayCanvas * canvas)
+ : LfpMonochromaticColourScheme(display, canvas)
+{
+ setName("Gradient");
+
+ baseHueLabel->setName("baseHueA");
+ baseHueLabel->setText("Hue A", dontSendNotification);
+
+ baseHueLabelB = new Label("baseHueB", "Hue B");
+ baseHueLabelB->setFont(Font("Default", 13.0f, Font::plain));
+ baseHueLabelB->setColour(Label::textColourId, Colour(100, 100, 100));
+ addAndMakeVisible(baseHueLabelB);
+
+ baseHueSliderB = new Slider;
+ baseHueSliderB->setRange(0, 1);
+ baseHueSliderB->setValue(0.5);
+ baseHueSliderB->setTextBoxStyle(Slider::NoTextBox, false, 0, 0);
+ baseHueSliderB->addListener(this);
+ addAndMakeVisible(baseHueSliderB);
+
+ baseHueSliderB->addMouseListener(this, true);
+
+ baseHueB = Colour::fromHSV(0.5, 1.0, 1.0, 1.0);
+ swatchHueB = baseHueB;
+
+ calculateColourSeriesFromBaseHue();
+}
+
+void LfpGradientColourScheme::paint(Graphics &g)
+{
+ g.setColour(swatchHue);
+ g.fillRect(colourSwatchRect);
+
+ g.setColour(swatchHueB);
+ g.fillRect(colourSwatchRectB);
+}
+
+void LfpGradientColourScheme::resized()
+{
+ numChannelsLabel->setBounds(0, 5, 120, 25);
+ numChannelsSelection->setBounds(numChannelsLabel->getRight(),
+ numChannelsLabel->getY(),
+ 60,
+ 25);
+
+ baseHueLabel->setBounds(0, numChannelsLabel->getBottom(), 35, 25);
+ baseHueSlider->setBounds(baseHueLabel->getRight(),
+ baseHueLabel->getY(),
+ numChannelsSelection->getRight() - baseHueLabel->getRight() - 20,
+ 25);
+
+ colourSwatchRect.setBounds(baseHueSlider->getRight() + 5, baseHueSlider->getY() + 5, 15, baseHueSlider->getHeight() - 10);
+
+ baseHueLabelB->setBounds(0, baseHueLabel->getBottom(), 35, 25);
+ baseHueSliderB->setBounds(baseHueLabelB->getRight(),
+ baseHueLabelB->getY(),
+ numChannelsSelection->getRight() - baseHueLabelB->getRight() - 20,
+ 25);
+
+ colourSwatchRectB.setBounds(baseHueSliderB->getRight() + 5, baseHueSliderB->getY() + 5, 15, baseHueSliderB->getHeight() - 10);
+
+ colourPatternLabel->setBounds(0, baseHueLabelB->getBottom(), 80, 25);
+ colourPatternSelection->setBounds(colourPatternLabel->getRight(),
+ colourPatternLabel->getY(),
+ numChannelsSelection->getRight() - colourPatternLabel->getRight(),
+ 25);
+}
+
+void LfpGradientColourScheme::sliderValueChanged(Slider *sl)
+{
+ if (sl == baseHueSlider)
+ {
+ swatchHue = Colour::fromHSV(sl->getValue(), 1, 1, 1);
+ repaint(colourSwatchRect);
+ }
+ else
+ {
+ swatchHueB = Colour::fromHSV(sl->getValue(), 1, 1, 1);
+ repaint(colourSwatchRectB);
+ }
+}
+
+void LfpGradientColourScheme::mouseUp(const MouseEvent &e)
+{
+ if (e.originalComponent == baseHueSlider)
+ {
+ if (swatchHue.getARGB() != baseHue.getARGB())
+ {
+ baseHue = swatchHue;
+ calculateColourSeriesFromBaseHue();
+ lfpDisplay->setColors();
+ canvas->redraw();
+ }
+ }
+ else
+ {
+ if (swatchHueB.getARGB() != baseHueB.getARGB())
+ {
+ baseHueB = swatchHueB;
+ calculateColourSeriesFromBaseHue();
+ lfpDisplay->setColors();
+ canvas->redraw();
+ }
+ }
+}
+
+void LfpGradientColourScheme::calculateColourSeriesFromBaseHue()
+{
+ colourList.clear();
+
+ int coloursToCalculate = numColourChannels;
+
+ if (numColourChannels % 2 == 0 && (colourPattern == DOWN_UP || colourPattern == UP_DOWN))
+ {
+ coloursToCalculate = coloursToCalculate / 2 + 1;
+ }
+
+ for (int i = 0; i < coloursToCalculate; ++i)
+ {
+ float hue = (baseHueB.getHue() - baseHue.getHue()) * i / float(coloursToCalculate - 1);
+ colourList.add(baseHue.withRotatedHue(hue));
+ }
+}
diff --git a/Plugins/LfpDisplayNode/LfpDisplayCanvas.h b/Plugins/LfpDisplayNode/LfpDisplayCanvas.h
index e4cfd2d0d7..2323752a75 100644
--- a/Plugins/LfpDisplayNode/LfpDisplayCanvas.h
+++ b/Plugins/LfpDisplayNode/LfpDisplayCanvas.h
@@ -20,39 +20,18 @@
along with this program. If not, see .
*/
-#ifndef __LFPDISPLAYCANVAS_H_Alpha__
-#define __LFPDISPLAYCANVAS_H_Alpha__
+#ifndef __LFPDISPLAYCANVAS_H__
+#define __LFPDISPLAYCANVAS_H__
#include
-#include "LfpDisplayNode.h"
#include
#include
-#define CHANNEL_TYPES 3
-#define MAX_N_CHAN 2048
-#define MAX_N_SAMP 5000
-#define MAX_N_SAMP_PER_PIXEL 100
-
+#include "LfpDisplayClasses.h"
+#include "LfpDisplayNode.h"
namespace LfpViewer {
-
-class LfpDisplayNode;
-
-class LfpTimescale;
-class LfpDisplay;
-class LfpChannelDisplay;
-class LfpChannelDisplayInfo;
-class EventDisplayInterface;
-class LfpViewport;
-class LfpDisplayOptions;
-class LfpBitmapPlotter;
-class PerPixelBitmapPlotter;
-class SupersampledBitmapPlotter;
-class LfpChannelColourScheme;
-
-
-
-#pragma mark - LfpDisplayCanvas -
+#pragma mark - LfpDisplayCanvas -
//==============================================================================
/**
@@ -218,925 +197,5 @@ class LfpDisplayCanvas : public Visualizer,
};
-
-
-#pragma mark - ShowHideOptionsButton -
-//==============================================================================
-/**
-
- Toggles view options drawer for LfpDisplayCanvas.
-
- */
-class ShowHideOptionsButton : public Button
-{
-public:
- ShowHideOptionsButton(LfpDisplayOptions*);
- virtual ~ShowHideOptionsButton();
- void paintButton(Graphics& g, bool, bool);
- LfpDisplayOptions* options;
-};
-
-
-
-#pragma mark - LfpDisplayOptions -
-//==============================================================================
-/**
-
- Holds the LfpDisplay UI controls
-
- */
-class LfpDisplayOptions : public Component,
- public Slider::Listener,
- public ComboBox::Listener,
- public Button::Listener
-{
-public:
- LfpDisplayOptions(LfpDisplayCanvas*, LfpTimescale*, LfpDisplay*, LfpDisplayNode*);
- ~LfpDisplayOptions();
-
- void paint(Graphics& g);
- void resized();
-
- void setRangeSelection(float range, bool canvasMustUpdate = false); // set range selection combo box to correct value if it has been changed by scolling etc.
- void setSpreadSelection(int spread, bool canvasMustUpdate = false, bool deferDisplayRefresh = false); // set spread selection combo box to correct value if it has been changed by scolling etc.
-
- void comboBoxChanged(ComboBox* cb);
- void buttonClicked(Button* button);
-
- /** Changes the timebase value used by LfpTimescale and LfpDisplayCanvas. */
- void setTimebaseAndSelectionText(float timebase);
-
- /** Handles slider events for all editors. */
- void sliderValueChanged(Slider* sl);
-
- /** Called by sliderValueChanged(). Deals with clicks on custom sliders. Subclasses
- of GenericEditor should modify this method only.*/
- void sliderEvent(Slider* sl);
-
- int getChannelHeight();
- bool getDrawMethodState();
- bool getInputInvertedState();
- bool getChannelNameState();
-
- /** Return a bool describing whether the spike raster functionality is enabled */
- bool getDisplaySpikeRasterizerState();
-
- /** Sets the state of the spike raster functionality on/off */
- void setDisplaySpikeRasterizerState(bool isEnabled);
-
- //void setRangeSelection(float range, bool canvasMustUpdate);
- void setSpreadSelection();
-
- void togglePauseButton(bool sendUpdate = true);
-
- void saveParameters(XmlElement* xml);
- void loadParameters(XmlElement* xml);
-
- DataChannel::DataChannelTypes getChannelType(int n);
- DataChannel::DataChannelTypes getSelectedType();
- String getTypeName(DataChannel::DataChannelTypes type);
- int getRangeStep(DataChannel::DataChannelTypes type);
-
- void setSelectedType(DataChannel::DataChannelTypes type, bool toggleButton = true);
-
- int selectedSpread;
- String selectedSpreadValue;
-
- int selectedTimebase;
- String selectedTimebaseValue;
-
- int selectedOverlap;
- String selectedOverlapValue;
-
- int selectedChannelDisplaySkip;
- String selectedChannelDisplaySkipValue;
-
- int selectedSpikeRasterThreshold;
- String selectedSpikeRasterThresholdValue;
-
- // this enum is a candidate option for refactoring, not used yet
- enum ChannelDisplaySkipValue {
- None = 0,
- One,
- Two,
- Four,
- Eight,
- Sixteen,
- ThirtyTwo
- } enum_selectedChannelDisplaySkipValue = None;
-
- int selectedSaturation; // for saturation warning
- String selectedSaturationValue;
- float selectedSaturationValueFloat; // TODO: this is way ugly - we should refactor all these parameters soon and get them into a nicer format- probably when we do the general plugin parameter overhaul.
-
-private:
-
- LfpDisplayCanvas* canvas;
- LfpDisplay* lfpDisplay;
- LfpTimescale* timescale;
- LfpDisplayNode* processor;
-
- Font labelFont;
- Colour labelColour;
-
- ScopedPointer timebaseSelection;
- ScopedPointer rangeSelection;
- ScopedPointer spreadSelection;
-
- ScopedPointer overlapSelection;
- ScopedPointer drawClipWarningButton; // optinally draw (subtle) warning if data is clipped in display
-
- ScopedPointer saturationWarningSelection;
- ScopedPointer drawSaturateWarningButton; // optionally raise hell if the actual data is saturating
-
- ScopedPointer colorGroupingSelection;
- ScopedPointer invertInputButton;
- ScopedPointer drawMethodButton;
- ScopedPointer pauseButton;
- OwnedArray typeButtons;
-
- // label and button for spike raster functionality
- ScopedPointer