diff --git a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp index f02b5662e193b1f8bcc56262d071d7f71ade5d6b..2251ed00ba28dc71e80cf74964d10b32b3805907 100644 --- a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp +++ b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp @@ -808,6 +808,8 @@ LfpDisplayOptions::LfpDisplayOptions(LfpDisplayCanvas* canvas_, LfpTimescale* ti channelZoomSliderLabel->setColour(Label::textColourId, labelColour); addAndMakeVisible(channelZoomSliderLabel); + + // init channel display skipping options channelDisplaySkipOptions.add("All"); channelDisplaySkipOptions.add("2"); @@ -831,24 +833,45 @@ LfpDisplayOptions::LfpDisplayOptions(LfpDisplayCanvas* canvas_, LfpTimescale* ti channelDisplaySkipLabel->setColour(Label::textColourId, labelColour); addAndMakeVisible(channelDisplaySkipLabel); + + // init stream rate displaying options - streamRateDisplayedOptions.add("High"); - streamRateDisplayedOptions.add("Low"); - selectedStreamRateDisplayed = 1; - selectedChannelDisplaySkipValue = streamRateDisplayedOptions[selectedStreamRateDisplayed - 1]; - - streamRateDisplayedSelection = new ComboBox("Displayed Stream Rate"); - streamRateDisplayedSelection->addItemList(streamRateDisplayedOptions, 1); - streamRateDisplayedSelection->setSelectedId(selectedStreamRateDisplayed, sendNotification); - streamRateDisplayedSelection->setEditableText(false); - streamRateDisplayedSelection->addListener(this); - addAndMakeVisible(streamRateDisplayedSelection); - - streamRateDisplayedLabel = new Label("Displayed Stream Rate Label", "Display Stream Rate"); - streamRateDisplayedLabel->setFont(labelFont); - streamRateDisplayedLabel->setColour(Label::textColourId, labelColour); - addAndMakeVisible(streamRateDisplayedLabel); +// streamRateDisplayedOptions.add("High"); +// streamRateDisplayedOptions.add("Low"); +// selectedStreamRateDisplayed = 1; +// selectedChannelDisplaySkipValue = streamRateDisplayedOptions[selectedStreamRateDisplayed - 1]; +// +// streamRateDisplayedSelection = new ComboBox("Displayed Stream Rate"); +// streamRateDisplayedSelection->addItemList(streamRateDisplayedOptions, 1); +// streamRateDisplayedSelection->setSelectedId(selectedStreamRateDisplayed, sendNotification); +// streamRateDisplayedSelection->setEditableText(false); +// streamRateDisplayedSelection->addListener(this); +// addAndMakeVisible(streamRateDisplayedSelection); +// +// streamRateDisplayedLabel = new Label("Displayed Stream Rate Label", "Display Stream Rate"); +// streamRateDisplayedLabel->setFont(labelFont); +// streamRateDisplayedLabel->setColour(Label::textColourId, labelColour); +// addAndMakeVisible(streamRateDisplayedLabel); + + + + // 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 show/hide options button showHideOptionsButton = new ShowHideOptionsButton(this); showHideOptionsButton->addListener(this); @@ -1107,7 +1130,7 @@ void LfpDisplayOptions::resized() 20); reverseChannelsDisplayLabel->setBounds(reverseChannelsDisplayButton->getRight(), reverseChannelsDisplayButton->getY(), - 180, + 120, 22); // Channel Display Skip Selector @@ -1121,14 +1144,25 @@ void LfpDisplayOptions::resized() 22); // Stream Rate Displayed Selector - streamRateDisplayedSelection->setBounds(reverseChannelsDisplayButton->getX() + 130, - channelDisplaySkipSelection->getY(), - 60, - 25); - streamRateDisplayedLabel->setBounds(streamRateDisplayedSelection->getX() - 5, - reverseChannelsDisplayButton->getY(), - 150, - 22); +// streamRateDisplayedSelection->setBounds(reverseChannelsDisplayButton->getX() + 130, +// channelDisplaySkipSelection->getY(), +// 60, +// 25); +// streamRateDisplayedLabel->setBounds(streamRateDisplayedSelection->getX() - 5, +// reverseChannelsDisplayButton->getY(), +// 150, +// 22); + + // Median Offset Plotting Button + medianOffsetPlottingButton->setBounds(reverseChannelsDisplayLabel->getRight() + 5, + reverseChannelsDisplayButton->getY(), + 20, + 20); + medianOffsetPlottingLabel->setBounds(medianOffsetPlottingButton->getRight(), + medianOffsetPlottingButton->getY(), + 150, + 22); + // Saturation Warning Selection saturationWarningSelection->setBounds(250, getHeight()-90, 60, 25); @@ -1264,9 +1298,15 @@ void LfpDisplayOptions::buttonClicked(Button* b) lfpDisplay->setChannelsReversed(b->getToggleState()); return; } + if (b == medianOffsetPlottingButton) + { + 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) @@ -1318,132 +1358,6 @@ void LfpDisplayOptions::comboBoxChanged(ComboBox* cb) { const int skipAmt = pow(2, cb->getSelectedId() - 1); lfpDisplay->setChannelDisplaySkipAmount(skipAmt); -// const int skipAmt = pow(2, cb->getSelectedId() - 2); -// -// if (skipAmt != 0) lfpDisplay->removeAllChildren(); // if show all -// -//// Array<LfpChannelDisplay*> channels; -//// Array<LfpChannelDisplayInfo*> channelInfo; -// -// Array<LfpDisplay::LfpChannel> &drawableChannels = lfpDisplay->drawableChannels; -// drawableChannels = Array<LfpDisplay::LfpChannel>(); -// -// Array<LfpDisplay::LfpChannel> channelsToDraw; -// -// // iterate over all channels and select drawable ones -// for (size_t i = 0; i < lfpDisplay->channels.size(); i++) -// { -// if (skipAmt == 0) // no skips, add all channels -// { -// lfpDisplay->channels[i]->setHidden(false); -// lfpDisplay->channelInfo[i]->setHidden(false); -// -// channelsToDraw.add(LfpDisplay::LfpChannel{ -// lfpDisplay->channels[i], -// lfpDisplay->channelInfo[i] -// }); -// -// lfpDisplay->addAndMakeVisible(lfpDisplay->channels[i]); -// lfpDisplay->addAndMakeVisible(lfpDisplay->channelInfo[i]); -// } -// else // skip some channels -// { -// if (i % (skipAmt + 1) == 0) // add these channels -// { -// lfpDisplay->channels[i]->setHidden(false); -// lfpDisplay->channelInfo[i]->setHidden(false); -// -// channelsToDraw.add(LfpDisplay::LfpChannel{ -// lfpDisplay->channels[i], -// lfpDisplay->channelInfo[i] -// }); -// -// lfpDisplay->addAndMakeVisible(lfpDisplay->channels[i]); -// lfpDisplay->addAndMakeVisible(lfpDisplay->channelInfo[i]); -// } -// else // but not these -// { -// lfpDisplay->channels[i]->setHidden(true); -// lfpDisplay->channelInfo[i]->setHidden(true); -// -// lfpDisplay->removeChildComponent(lfpDisplay->channels[i]); -// lfpDisplay->removeChildComponent(lfpDisplay->channelInfo[i]); -// } -// } -// } -// -// // check if channels should be added to drawableChannels in reverse -// if (lfpDisplay->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]); -// } -// } -// -//// if (lfpDisplay->getChannelsReversed()) -//// { -//// for (int i = lfpDisplay->getNumChannels() - 1; i >= 0; --i) -//// { -////// channels.add(lfpDisplay->channels[i]); -////// channelInfo.add(lfpDisplay->channelInfo[i]); -//// drawableChannels.add(LfpDisplay::LfpChannel{lfpDisplay->channels[i], lfpDisplay->channelInfo[i]}); -//// } -//// std::cout << "Size of drawableChannels array: " << drawableChannels.size() << std::endl; -//// } -//// else -//// { -////// channel.addArray(lfpDisplay->channels); -////// channelInfo.addArray(lfpDisplay->channelInfo); -//// for (size_t i = 0; i < lfpDisplay->getNumChannels(); ++i) -//// { -//// drawableChannels.add(LfpDisplay::LfpChannel{lfpDisplay->channels[i], lfpDisplay->channelInfo[i]}); -//// } -//// } -//// -////// int numVisible = 0; -//// for (size_t i = 0; i < lfpDisplay->channels.size(); i++) -//// { -//// if (skipAmt == 0) -//// { -//// channels[i]->setHidden(false); -//// channelInfo[i]->setHidden(false); -//// -//// lfpDisplay->addAndMakeVisible(channels[i]); -//// lfpDisplay->addAndMakeVisible(channelInfo[i]); -//// ++numVisible; -//// } -//// else -//// { -//// bool shouldBeVisible = (i % (skipAmt + 1) == 0); -//// -//// channels[i]->setHidden(!shouldBeVisible); -//// channelInfo[i]->setHidden(!shouldBeVisible); -//// -//// if (shouldBeVisible) -//// { -//// lfpDisplay->addAndMakeVisible(channels[i]); -//// lfpDisplay->addAndMakeVisible(channelInfo[i]); -//// ++numVisible; -//// } -////// else -////// { -////// lfpDisplay->removeChildComponent(lfpDisplay->channels[i]); -////// lfpDisplay->removeChildComponent(lfpDisplay->channelInfo[i]); -////// } -//// } -//// } -// -// -//// canvas->resized(); -// canvas->resizeToChannels(); } else if (cb == timebaseSelection) { @@ -1926,6 +1840,12 @@ LfpDisplay::LfpDisplay(LfpDisplayCanvas* c, Viewport* v) , channelsReversed(false) , displaySkipAmt(0) { + perPixelPlotter = new PerPixelBitmapPlotter(this); +// histogramPlotter = new HistogramBitmapPlotter(this); + + plotter = perPixelPlotter; + m_MedianOffsetPlottingFlag = false; + totalHeight = 0; colorGrouping=1; @@ -2311,6 +2231,16 @@ void LfpDisplay::setDrawMethod(bool isDrawMethod) { channels[i]->setDrawMethod(isDrawMethod); } + + if (isDrawMethod) + { +// plotter = histogramPlotter; + } + else + { + plotter = perPixelPlotter; + } + resized(); } @@ -2411,6 +2341,16 @@ void LfpDisplay::setChannelDisplaySkipAmount(int skipAmt) rebuildDrawableChannelsList(); } +bool LfpDisplay::getMedianOffsetPlotting() +{ + return m_MedianOffsetPlottingFlag; +} + +void LfpDisplay::setMedianOffsetPlotting(bool isEnabled) +{ + m_MedianOffsetPlottingFlag = isEnabled; +} + void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel) { @@ -2658,6 +2598,11 @@ void LfpDisplay::rebuildDrawableChannelsList() canvas->resizeToChannels(); } +LfpBitmapPlotter * const LfpDisplay::getPlotterPtr() const +{ + return plotter; +} + bool LfpDisplay::getSingleChannelState() { //if (singleChan < 0) return false; @@ -2907,6 +2852,10 @@ void LfpChannelDisplay::pxPaint() 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 { @@ -2967,6 +2916,14 @@ void LfpChannelDisplay::pxPaint() 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; @@ -3086,29 +3043,38 @@ void LfpChannelDisplay::pxPaint() else //drawmethod { // simple per-pixel min-max drawing, has no anti-aliasing, but runs faster - int jfrom=from+getY(); - int jto=to+getY(); - - //if (yofs<0) {yofs=0;}; - - if (i<0) {i=0;}; - if (i >= display->lfpChannelBitmap.getWidth()) {i = 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 + plotterInfo.channelID = chan; + plotterInfo.y = getY(); + plotterInfo.from = from; + plotterInfo.to = to; + plotterInfo.samp = i; + plotterInfo.lineColour = lineColour; - if (jfrom<0) {jfrom=0;}; - if (jto >= display->lfpChannelBitmap.getHeight()) {jto=display->lfpChannelBitmap.getHeight()-1;}; + // TODO: (kelly) complete transition toward plotter class encapsulation + display->getPlotterPtr()->plot(bdLfpChannelBitmap, plotterInfo); // plotterInfo is prepared above - - 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; - - bdLfpChannelBitmap.setPixelColour(i,j,lineColour); - - } +// int jfrom=from+getY(); +// int jto=to+getY(); +// +// //if (yofs<0) {yofs=0;}; +// +// if (i<0) {i=0;}; +// if (i >= display->lfpChannelBitmap.getWidth()) {i = 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; +// +// bdLfpChannelBitmap.setPixelColour(i, j, lineColour); +// +// } } @@ -3584,3 +3550,36 @@ void LfpViewport::visibleAreaChanged(const Rectangle<int>& newVisibleArea) canvas->fullredraw = true; canvas->refresh(); } + +#pragma mark - PerPixelBitmapPlotter - + +PerPixelBitmapPlotter::PerPixelBitmapPlotter(LfpDisplay * lfpDisplay) + : LfpBitmapPlotter(lfpDisplay) +{ } + +void PerPixelBitmapPlotter::plot(Image::BitmapData &bitmapData, LfpBitmapPlotterInfo &plotterInfo) +{ + int jfrom = plotterInfo.from + plotterInfo.y; + int jto = plotterInfo.to + plotterInfo.y; + + //if (yofs<0) {yofs=0;}; + + if (plotterInfo.samp < 0) {plotterInfo.samp = 0;}; + if (plotterInfo.samp >= display->lfpChannelBitmap.getWidth()) {plotterInfo.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(plotterInfo.samp,j,plotterInfo.lineColour); + + } +} diff --git a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h index 29bee0f037be3d1b36c955c3c0e8b17855e36302..da3627e65fc4f03ff52c9d7da5a1667728502fbe 100644 --- a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h +++ b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h @@ -45,6 +45,8 @@ class LfpChannelDisplayInfo; class EventDisplayInterface; class LfpViewport; class LfpDisplayOptions; +class LfpBitmapPlotter; +class PerPixelBitmapPlotter; #pragma mark - LfpDisplayCanvas - //============================================================================== @@ -337,6 +339,10 @@ private: ScopedPointer<ComboBox> streamRateDisplayedSelection; StringArray streamRateDisplayedOptions; + // label and toggle button for the median offset plotting feature + ScopedPointer<Label> medianOffsetPlottingLabel; + ScopedPointer<UtilityButton> medianOffsetPlottingButton; + ScopedPointer<Slider> brightnessSliderA; ScopedPointer<Slider> brightnessSliderB; @@ -472,6 +478,12 @@ public: 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 a single channel is focused in viewport */ bool getSingleChannelState(); @@ -492,6 +504,9 @@ public: /** 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; @@ -547,11 +562,17 @@ private: int colorGrouping; bool channelsReversed; + bool m_MedianOffsetPlottingFlag; LfpDisplayCanvas* canvas; Viewport* viewport; float range[3]; + + LfpBitmapPlotter * plotter; + + ScopedPointer<PerPixelBitmapPlotter> perPixelPlotter; + // ScopedPointer<HistogramBitmapPlotter> histogramPlotter; }; @@ -766,6 +787,58 @@ public: private: LfpDisplayCanvas* canvas; }; + +#pragma mark - LfpBitmapPlotterInfo - +//============================================================================== +/** + Information struct for plotting method encapsulation classes. + */ +struct LfpBitmapPlotterInfo +{ + int channelID; + int samp; + int to; + int from; + int x; + int y; + Colour lineColour; +}; + +#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; +}; + +#pragma mark - PerPixelBitmapPlotter - +//============================================================================== +/** + Abstraction of the per-pixel plotting method. + */ +class PerPixelBitmapPlotter : public LfpBitmapPlotter +{ +public: + PerPixelBitmapPlotter(LfpDisplay * lfpDisplay); + virtual ~PerPixelBitmapPlotter() {} + + /** Plots one subsample of data from a single channel to the bitmap provided */ + virtual void plot(Image::BitmapData &bitmapData, LfpBitmapPlotterInfo &plotterInfo) override; +}; + }; #endif // __LFPDISPLAYCANVAS_H_Alpha__