From a88a6c119be081f94916c70c29dc40c496dabf38 Mon Sep 17 00:00:00 2001 From: kmichaelfox <kmichaelfox.contact@gmail.com> Date: Thu, 31 Aug 2017 15:46:37 -0700 Subject: [PATCH] add Cmd+<mouse drag> on LfpDisplayChannelInfo components for zoom, to replace slider --- .../LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp | 193 +++++++++++++++--- .../LfpDisplayNodeAlpha/LfpDisplayCanvas.h | 93 +++++++-- 2 files changed, 245 insertions(+), 41 deletions(-) diff --git a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp index ad68cdedb..68a6a2643 100644 --- a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp +++ b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.cpp @@ -562,6 +562,12 @@ bool LfpDisplayCanvas::getDrawMethodState() return options->getDrawMethodState(); //drawMethodButton->getToggleState(); } +int LfpDisplayCanvas::getChannelSampleRate(int channel) +{ + // TODO: (kelly) should this do a range check? + return sampleRate[channel]; +} + void LfpDisplayCanvas::redraw() { fullredraw=true; @@ -824,6 +830,24 @@ LfpDisplayOptions::LfpDisplayOptions(LfpDisplayCanvas* canvas_, LfpTimescale* ti channelDisplaySkipLabel->setFont(labelFont); 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); // init show/hide options button showHideOptionsButton = new ShowHideOptionsButton(this); @@ -1067,32 +1091,45 @@ void LfpDisplayOptions::resized() pauseButton->setBounds(450,getHeight()-50,50,44); // Channel Zoom Slider - channelZoomSlider->setBounds(pauseButton->getRight() + 10, getHeight() - 30, 100, 22); + channelZoomSlider->setBounds(pauseButton->getRight() + 5, + getHeight() - 30, + 100, + 22); channelZoomSliderLabel->setBounds(channelZoomSlider->getX(), channelZoomSlider->getY() - 20, 180, 22); // Reverse Channels Display - reverseChannelsDisplayButton->setBounds(channelZoomSlider->getX() + channelZoomSlider->getWidth() + 25, + reverseChannelsDisplayButton->setBounds(channelZoomSlider->getX() + channelZoomSlider->getWidth() + 5, getHeight()-50, 20, 20); - reverseChannelsDisplayLabel->setBounds(reverseChannelsDisplayButton->getRight() + 5, + reverseChannelsDisplayLabel->setBounds(reverseChannelsDisplayButton->getRight(), reverseChannelsDisplayButton->getY(), 180, 22); // Channel Display Skip Selector - channelDisplaySkipSelection->setBounds(channelZoomSlider->getX() + channelZoomSlider-> getWidth() + 25, + channelDisplaySkipSelection->setBounds(channelZoomSlider->getX() + channelZoomSlider-> getWidth() + 5, channelZoomSlider->getY(), 60, 25); - channelDisplaySkipLabel->setBounds(channelDisplaySkipSelection->getRight() + 5, - channelDisplaySkipSelection->getY(), + channelDisplaySkipLabel->setBounds(channelDisplaySkipSelection->getRight(), + channelDisplaySkipSelection->getY() + 2, 100, 22); - + + // Stream Rate Displayed Selector + streamRateDisplayedSelection->setBounds(reverseChannelsDisplayButton->getX() + 130, + channelDisplaySkipSelection->getY(), + 60, + 25); + streamRateDisplayedLabel->setBounds(streamRateDisplayedSelection->getX() - 5, + reverseChannelsDisplayButton->getY(), + 150, + 22); + // Saturation Warning Selection saturationWarningSelection->setBounds(250, getHeight()-90, 60, 25); @@ -1194,6 +1231,7 @@ void LfpDisplayOptions::setRangeSelection(float range, bool canvasMustUpdate) void LfpDisplayOptions::setSpreadSelection(int spread, bool canvasMustUpdate) { + if (canvasMustUpdate) { spreadSelection->setText(String(spread),sendNotification); @@ -1989,12 +2027,18 @@ void LfpDisplay::setNumChannels(int numChannels) //lfpInfo->setColour(channelColours[i % channelColours.size()]); lfpInfo->setRange(range[options->getChannelType(i)]); lfpInfo->setChannelHeight(canvas->getChannelHeight()); + + // TODO: (kelly) this won't work... samplerate gets set AFTER this method is called + lfpInfo->setChannelSampleRate(canvas->getChannelSampleRate(i)); addAndMakeVisible(lfpInfo); channelInfo.add(lfpInfo); - drawableChannels.add(LfpChannel{lfpChan, lfpInfo}); + drawableChannels.add(LfpChannelTrack{ + lfpChan, + lfpInfo + }); savedChannelState.add(true); @@ -2394,8 +2438,22 @@ void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& 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(h+hdiff); + setChannelHeight(newHeight); int oldX=viewport->getViewPositionX(); int oldY=viewport->getViewPositionY(); @@ -2405,7 +2463,7 @@ void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& 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(h+hdiff); // update combobox + options->setSpreadSelection(newHeight); // update combobox canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw } @@ -2450,8 +2508,6 @@ void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& void LfpDisplay::toggleSingleChannel(int chan) { - // TODO: (kelly) this breaks with the new reversing and filtering mechanisms - //std::cout << "Toggle channel " << chan << std::endl; @@ -2476,15 +2532,15 @@ void LfpDisplay::toggleSingleChannel(int chan) // channels[i]->setEnabledState(false); // } - std::cout << "\nSingle channel on (" << chan << ")" << std::endl; + std::cout << "Single channel on (" << chan << ")" << std::endl; singleChan = chan; int newHeight = viewport->getHeight(); - LfpChannel lfpChannel{drawableChannels[chan].channel, drawableChannels[chan].channelInfo}; + LfpChannelTrack lfpChannelTrack{drawableChannels[chan].channel, drawableChannels[chan].channelInfo}; // drawableChannels[chan].channelInfo->setEnabledState(true); // drawableChannels[chan].channelInfo->setSingleChannelState(true); - lfpChannel.channelInfo->setEnabledState(true); - lfpChannel.channelInfo->setSingleChannelState(true); + lfpChannelTrack.channelInfo->setEnabledState(true); + lfpChannelTrack.channelInfo->setSingleChannelState(true); setChannelHeight(newHeight, false); setSize(getWidth(), numChans*getChannelHeight()); @@ -2492,24 +2548,26 @@ void LfpDisplay::toggleSingleChannel(int chan) viewport->setViewPosition(Point<int>(0, chan*newHeight)); // viewport->setViewPosition(Point<int>(0, 0)); + // disable unused channels for (int i = 0; i < drawableChannels.size(); i++) { if (i != chan) drawableChannels[i].channel->setEnabledState(false); } - Array<LfpChannel> channelsToDraw{lfpChannel}; + // update drawableChannels, give only the single channel to focus on + Array<LfpChannelTrack> channelsToDraw{lfpChannelTrack}; drawableChannels = channelsToDraw; // remove all other children and show this one channel removeAllChildren(); - addAndMakeVisible(lfpChannel.channel); - addAndMakeVisible(lfpChannel.channelInfo); + addAndMakeVisible(lfpChannelTrack.channel); + addAndMakeVisible(lfpChannelTrack.channelInfo); } // else if (chan == singleChan || chan == -2) - else if (getSingleChannelState()) + else { - std::cout << "\nSingle channel off" << std::endl; + std::cout << "Single channel off" << std::endl; for (int n = 0; n < numChans; n++) { channelInfo[n]->setSingleChannelState(false); @@ -2533,12 +2591,10 @@ void LfpDisplay::reactivateChannels() void LfpDisplay::rebuildDrawableChannelsList() { - // TODO: (kelly) possible test here for singleChannel turned ON - if (displaySkipAmt != 0) removeAllChildren(); // start with clean slate - Array<LfpChannel> channelsToDraw; - drawableChannels = Array<LfpDisplay::LfpChannel>(); + Array<LfpChannelTrack> channelsToDraw; + drawableChannels = Array<LfpDisplay::LfpChannelTrack>(); // iterate over all channels and select drawable ones @@ -2549,7 +2605,7 @@ void LfpDisplay::rebuildDrawableChannelsList() channels[i]->setHidden(false); channelInfo[i]->setHidden(false); - channelsToDraw.add(LfpDisplay::LfpChannel{ + channelsToDraw.add(LfpDisplay::LfpChannelTrack{ channels[i], channelInfo[i] }); @@ -2564,7 +2620,7 @@ void LfpDisplay::rebuildDrawableChannelsList() channels[i]->setHidden(false); channelInfo[i]->setHidden(false); - channelsToDraw.add(LfpDisplay::LfpChannel{ + channelsToDraw.add(LfpDisplay::LfpChannelTrack{ channels[i], channelInfo[i] }); @@ -3300,6 +3356,89 @@ 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) + { + display->trackZoomInfo.isScrollingY = true; + display->trackZoomInfo.componentStartHeight = getChannelHeight(); + display->trackZoomInfo.zoomPivotRatioY = (getY() + e.getMouseDownY())/(float)display->getHeight(); + display->trackZoomInfo.zoomPivotRatioX = (getX() + e.getMouseDownX())/(float)display->getWidth(); + display->trackZoomInfo.zoomPivotViewportOffset = getPosition() + e.getMouseDownPosition() - canvas->viewport->getViewPosition(); + + } + + 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; + } + + // set channel heights for all channel + display->setChannelHeight(newHeight); + display->setBounds(0,0,display->getWidth()-0, display->getChannelHeight()*display->drawableChannels.size()); // update height so that the scrollbar is correct + + canvas->viewport->setViewPositionProportionately(display->trackZoomInfo.zoomPivotRatioX, display->trackZoomInfo.zoomPivotRatioY); + + 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(display->trackZoomInfo.zoomPivotRatioX, newViewportY); + + options->setSpreadSelection(newHeight); // update combobox + + canvas->fullredraw = true;//issue full redraw - scrolling without modifier doesnt require a full redraw + } + } +} + +void LfpChannelDisplayInfo::mouseUp(const MouseEvent &e) +{ + if (e.mods.isLeftButtonDown()) + { + display->trackZoomInfo.isScrollingY = false; + } +} + void LfpChannelDisplayInfo::paint(Graphics& g) { diff --git a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h index db8858d93..29bee0f03 100644 --- a/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h +++ b/Source/Plugins/LfpDisplayNodeAlpha/LfpDisplayCanvas.h @@ -46,6 +46,7 @@ class EventDisplayInterface; class LfpViewport; class LfpDisplayOptions; +#pragma mark - LfpDisplayCanvas - //============================================================================== /** @@ -76,7 +77,8 @@ public: void refresh(); void resized(); - /** Resizes the LfpDisplay to the size required to fit all channels + /** Resizes the LfpDisplay to the size required to fit all channels that are being + drawn to the screen. @param respectViewportPosition (optional) if true, viewport automatically scrolls to maintain view prior to resizing @@ -97,6 +99,8 @@ public: int getNumChannelsVisible(); bool getInputInvertedState(); bool getDrawMethodState(); + + int getChannelSampleRate(int channel); const float getXCoord(int chan, int samp); const float getYCoord(int chan, int samp); @@ -192,6 +196,7 @@ private: }; +#pragma mark - ShowHideOptionsButton - //============================================================================== /** @@ -206,7 +211,14 @@ public: 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, @@ -262,6 +274,11 @@ public: int selectedChannelDisplaySkip; String selectedChannelDisplaySkipValue; + + int selectedStreamRateDisplayed; + String selectedStreamRateDisplayedValue; + + // this enum is a candidate option for refactoring, not used yet enum ChannelDisplaySkipValue { None = 0, One, @@ -315,6 +332,11 @@ private: ScopedPointer<ComboBox> channelDisplaySkipSelection; StringArray channelDisplaySkipOptions; + // label and combobox for stream rate to be displayed (only show one or other) + ScopedPointer<Label> streamRateDisplayedLabel; + ScopedPointer<ComboBox> streamRateDisplayedSelection; + StringArray streamRateDisplayedOptions; + ScopedPointer<Slider> brightnessSliderA; ScopedPointer<Slider> brightnessSliderB; @@ -323,6 +345,8 @@ private: ScopedPointer<ShowHideOptionsButton> showHideOptionsButton; + // TODO: (kelly) consider moving these into a config singleton (meyers?) to clean up + // the constructor and huge array inits currently in there StringArray voltageRanges[CHANNEL_TYPES]; StringArray timebases; StringArray spreads; // option for vertical spacing between channels @@ -341,8 +365,9 @@ private: OwnedArray<EventDisplayInterface> eventDisplayInterfaces; }; - + +#pragma mark - LfpTimeScale - //============================================================================== /** @@ -372,6 +397,7 @@ private: }; +#pragma mark - LfpDisplay - //============================================================================== /** @@ -458,7 +484,9 @@ public: @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 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); @@ -469,22 +497,40 @@ public: Array<Colour> channelColours; - Array<LfpChannelDisplay*> channels; - Array<LfpChannelDisplayInfo*> channelInfo; + Array<LfpChannelDisplay*> channels; // all channels + Array<LfpChannelDisplayInfo*> channelInfo; // all channelInfos /** Convenience struct for holding a channel and its info in drawableChannels */ - struct LfpChannel + struct LfpChannelTrack { LfpChannelDisplay * channel; LfpChannelDisplayInfo * channelInfo; }; - Array<LfpChannel> drawableChannels; // holds the channels and info that are - // drawable to the screen + Array<LfpChannelTrack> drawableChannels; // holds the channels and info that are + // drawable to the screen bool eventDisplayEnabled[8]; - bool isPaused; // simple pause function, skips screen bufer updates + 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 + int componentStartWidth; + float zoomPivotRatioX; // a cache for calculating the anchor point when adjusting viewport + float zoomPivotRatioY; + Point<int> zoomPivotViewportOffset; // similar to above, but pixel-wise offset + } + trackZoomInfo; // and create an instance here private: @@ -494,7 +540,7 @@ private: int numChans; int displaySkipAmt; - int cachedDisplayChannelHeight; + int cachedDisplayChannelHeight; // holds a channel height if reset during single channel focus int totalHeight; @@ -509,7 +555,8 @@ private: }; - + +#pragma mark - LfpChannelDisplay - //============================================================================== /** Displays the information pertaining to a single data channel. @@ -614,7 +661,8 @@ protected: }; - + +#pragma mark - LfpChannelDisplayInfo - //============================================================================== /** Displays meta data pertaining to an associated channel, such as channel number. @@ -641,17 +689,32 @@ public: 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); + + /** 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; + ScopedPointer<UtilityButton> enableButton; }; +#pragma mark - EventDisplayInterface - //============================================================================== /** Interface class for Event Display channels. @@ -681,7 +744,9 @@ private: ScopedPointer<UtilityButton> chButton; }; - + + +#pragma mark - LfpViewport - //============================================================================== /** Encapsulates the logic for the LfpDisplayCanvas's viewable area and user inter- -- GitLab