diff --git a/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate b/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate index 08a70b2fb0568f9c81af25d62bf13e3c94572cf4..18f14965ee80b51b5296b80fbdd9024fafcdf97d 100755 Binary files a/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate and b/Builds/MacOSX/open-ephys.xcodeproj/project.xcworkspace/xcuserdata/Josh.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Source/Processors/DataThreads/FileReaderThread.cpp b/Source/Processors/DataThreads/FileReaderThread.cpp index 3b5b8c04b2767465a4f5ab0263a825466b6e7300..553725472d38c2656ecdedd1ce90d4e844726c16 100755 --- a/Source/Processors/DataThreads/FileReaderThread.cpp +++ b/Source/Processors/DataThreads/FileReaderThread.cpp @@ -137,7 +137,7 @@ bool FileReaderThread::updateBuffer() for (int n = 0; n < bufferSize; n++) { - thisSample[chan] = float(-readBuffer[n]); + thisSample[chan] = float(-readBuffer[n])*0.035; if (chan == 15) { diff --git a/Source/Processors/Editors/GenericEditor.cpp b/Source/Processors/Editors/GenericEditor.cpp index 9c77c1c990a08165ce114e48a2302ea5a60fc740..28f5f8bcf739e8cc683d5cad6f2313ad55e36983 100755 --- a/Source/Processors/Editors/GenericEditor.cpp +++ b/Source/Processors/Editors/GenericEditor.cpp @@ -769,22 +769,27 @@ void TriangleButton::paintButton(Graphics& g, bool isMouseOver, bool isButtonDow } -void GenericEditor::updateParameterButtons(int parameterIndex=-1){ +void GenericEditor::updateParameterButtons(int parameterIndex) +{ + if (parameterEditors.size()==0) { //Checks if there is a parameter editor, and stops a bug if there isn't. std::cout << "No parameterEditors" << std::endl; } - else{ - if (parameterIndex==-1){ - for (int i = 0; i < parameterEditors.size(); ++i) + else + { + if (parameterIndex == -1) { - parameterEditors[i]->channelSelectionUI(); + for (int i = 0; i < parameterEditors.size(); ++i) + { + parameterEditors[i]->channelSelectionUI(); + } + } + else + { + parameterEditors[parameterIndex]->channelSelectionUI(); } - } - else{ - parameterEditors[parameterIndex]->channelSelectionUI(); - } std::cout << "updateParameterButtons" << std::endl; } } diff --git a/Source/Processors/Editors/GenericEditor.h b/Source/Processors/Editors/GenericEditor.h index 214bae945e2b40cca95609c757cc7d7ae7cde4c6..c8c59178b25dcf4e1c310ff5869d924bd6bdf9a1 100755 --- a/Source/Processors/Editors/GenericEditor.h +++ b/Source/Processors/Editors/GenericEditor.h @@ -238,7 +238,7 @@ public: virtual void loadEditorParameters(XmlElement* xml); /** Syncs parametereditor colors with parameter values */ - void updateParameterButtons(int parameterIndex); + void updateParameterButtons(int parameterIndex = -1); protected: /** A pointer to the button that opens the drawer for the ChannelSelector. */ diff --git a/Source/Processors/Editors/LfpDisplayEditor.cpp b/Source/Processors/Editors/LfpDisplayEditor.cpp index 42a6712a657359adb8fc308ad3f7cf15162ae11e..b5564dd525351bf80d48253334697989aa51128b 100755 --- a/Source/Processors/Editors/LfpDisplayEditor.cpp +++ b/Source/Processors/Editors/LfpDisplayEditor.cpp @@ -63,3 +63,4 @@ void LfpDisplayEditor::buttonCallback(Button* button) } } + diff --git a/Source/Processors/RecordNode.cpp b/Source/Processors/RecordNode.cpp index 0d3bfdf1bfa3c1f2b963681b5c5168e1659995dd..0e79b4d282b08ba11fd68f6a98fc371bf0ba42b7 100755 --- a/Source/Processors/RecordNode.cpp +++ b/Source/Processors/RecordNode.cpp @@ -286,7 +286,7 @@ String RecordNode::generateDateString() void RecordNode::setParameter(int parameterIndex, float newValue) { - editor->updateParameterButtons(parameterIndex); + //editor->updateParameterButtons(parameterIndex); // 0 = stop recording // 1 = start recording diff --git a/Source/Processors/Visualization/SpikeDisplayCanvas.cpp b/Source/Processors/Visualization/SpikeDisplayCanvas.cpp index 72f2fe5129241cf1c0762066e3bad0566e79e271..6267bc67ef541043a04a85727d3a8e4ffe088147 100755 --- a/Source/Processors/Visualization/SpikeDisplayCanvas.cpp +++ b/Source/Processors/Visualization/SpikeDisplayCanvas.cpp @@ -463,18 +463,18 @@ void SpikePlot::setLimitsOnAxes() { //std::cout<<"SpikePlot::setLimitsOnAxes()"<<std::endl; - for (int i = 0; i < nWaveAx; i++) - wAxes[i]->setYLims(limits[i][0], limits[i][1]); - - // Each Projection sets its limits using the limits of the two waveform dims it represents. - // Convert projection number to indecies, and then set the limits using those indices - int j1, j2; - for (int i = 0; i < nProjAx; i++) - { - n2ProjIdx(pAxes[i]->getType(), &j1, &j2); - pAxes[i]->setYLims(limits[j1][0], limits[j1][1]); - pAxes[i]->setXLims(limits[j2][0], limits[j2][1]); - } + // for (int i = 0; i < nWaveAx; i++) + // wAxes[i]->setYLims(limits[i][0], limits[i][1]); + + // // Each Projection sets its limits using the limits of the two waveform dims it represents. + // // Convert projection number to indecies, and then set the limits using those indices + // int j1, j2; + // for (int i = 0; i < nProjAx; i++) + // { + // n2ProjIdx(pAxes[i]->getType(), &j1, &j2); + // pAxes[i]->setYLims(limits[j1][0], limits[j1][1]); + // pAxes[i]->setXLims(limits[j2][0], limits[j2][1]); + // } } void SpikePlot::initLimits() @@ -558,56 +558,29 @@ void SpikePlot::zoom(int dim, bool in) } -void SpikePlot::n2ProjIdx(int proj, int* p1, int* p2) -{ - int d1, d2; - if (proj==PROJ1x2) - { - d1 = 0; - d2 = 1; - } - else if (proj==PROJ1x3) - { - d1 = 0; - d2 = 2; - } - else if (proj==PROJ1x4) - { - d1 = 0; - d2 = 3; - } - else if (proj==PROJ2x3) - { - d1 = 1; - d2 = 2; - } - else if (proj==PROJ2x4) - { - d1 = 1; - d2 = 3; - } - else if (proj==PROJ3x4) - { - d1 = 2; - d2 = 3; - } - else - { - std::cout<<"Invalid projection:"<<proj<<"! Cannot determine d1 and d2"<<std::endl; - *p1 = -1; - *p2 = -1; - return; - } - *p1 = d1; - *p2 = d2; -} + // -------------------------------------------------- -WaveAxes::WaveAxes(int channel) : GenericAxes(channel), drawGrid(true) +WaveAxes::WaveAxes(int channel) : GenericAxes(channel), drawGrid(true), + bufferSize(10), spikeIndex(0), thresholdLevel(0.5f), + isOverThresholdSlider(false), isDraggingThresholdSlider(false) { + + addMouseListener(this, true); + + thresholdColour = Colours::red; + font = Font("Small Text",10,Font::plain); + + for (int n = 0; n < bufferSize; n++) + { + SpikeObject so; + generateEmptySpike(&so, 4); + + spikeBuffer.add(so); + } } void WaveAxes::paint(Graphics& g) @@ -617,133 +590,376 @@ void WaveAxes::paint(Graphics& g) int chan = 0; + // draw the grid lines for the waveforms + + if (drawGrid) + drawWaveformGrid(s.threshold[chan], s.gain[chan], g); + + // draw the threshold line and labels + drawThresholdSlider(g); + // if no spikes have been received then don't plot anything if (!gotFirstSpike) { return; } + + + for (int spikeNum = 0; spikeNum < bufferSize; spikeNum++) + { + + if (spikeNum != spikeIndex) + { + g.setColour(Colours::grey); + plotSpike(spikeBuffer[spikeNum], g); + } + + } + + g.setColour(Colours::white); + plotSpike(spikeBuffer[spikeIndex], g); + + + + +} + +void WaveAxes::plotSpike(const SpikeObject& s, Graphics& g) +{ float h = getHeight(); - // draw the grid lines for the waveforms - if (drawGrid) - drawWaveformGrid(s.threshold[chan], s.gain[chan], g); //compute the spatial width for each waveform sample - float dx = (getWidth()-10)/s.nSamples; - float x = 5.0f; - + float dx = (getWidth()-10)/float(spikeBuffer[0].nSamples); + // type corresponds to channel so we need to calculate the starting // sample based upon which channel is getting plotted - int sampIdx = s.nSamples * type; // - - // draw the individual waveform points - g.setColour(Colours::white); + int sampIdx = 40*type; //spikeBuffer[0].nSamples * type; // int dSamples = 1; - for (int i = 0; i < s.nSamples-1; i++) + + float x = 5.0f; + + for (int i = 0; i < s.nSamples-1; i++) { //std::cout << s.data[sampIdx] << std::endl; - g.drawLine(x, h/2 + (s.data[sampIdx]-32768)/100, x+dx, h/2 + (s.data[sampIdx+1]-32768)/100); + g.drawLine(x, + h/2 + (s.data[sampIdx]-32768)/100, + x+dx, + h/2 + (s.data[sampIdx+1]-32768)/100); sampIdx += dSamples; x += dx; } - // draw the threshold line and labels +} + +void WaveAxes::drawThresholdSlider(Graphics& g) +{ + + float h = getHeight()*thresholdLevel; + + g.setColour(thresholdColour); + g.drawLine(5.0f, h, getWidth()-5.0f, h); } void WaveAxes::drawWaveformGrid(int threshold, int gain, Graphics& g) { - double voltRange = ylims[1] - ylims[0]; - double pixelRange = getHeight(); - //This is a totally arbitrary value that seemed to lok the best for me - int minPixelsPerTick = 25; - int MAX_N_TICKS = 10; - int nTicks = pixelRange / minPixelsPerTick; - while (nTicks > MAX_N_TICKS) + float h = getHeight(); + float w = getWidth(); + + for (int i = 1; i < 10; i++) { - minPixelsPerTick += 5; - nTicks = pixelRange / minPixelsPerTick; + g.setColour(Colours::darkgrey); + + g.drawLine(5.0,h/10*i,w-5.0f,h/10*i); + } - int voltPerTick = (voltRange / nTicks); + // double voltRange = ylims[1] - ylims[0]; + // double pixelRange = getHeight(); + // //This is a totally arbitrary value that seemed to lok the best for me + // int minPixelsPerTick = 25; + // int MAX_N_TICKS = 10; - g.setColour(Colours::red); - char cstr[200] = {0}; - String str; + // int nTicks = pixelRange / minPixelsPerTick; + // while (nTicks > MAX_N_TICKS) + // { + // minPixelsPerTick += 5; + // nTicks = pixelRange / minPixelsPerTick; + // } - double tickVoltage = (double) threshold; + // int voltPerTick = (voltRange / nTicks); - // If the limits are bad we don't want to hang the program trying to draw too many ticks - // so count the number of ticks drawn and kill the routine after 100 draws - int tickCount=0; - while (tickVoltage < ylims[1] - voltPerTick*1.5) // Draw the ticks above the thold line - { - tickVoltage = (double) roundUp(tickVoltage + voltPerTick, 100); + // g.setColour(Colours::red); + // char cstr[200] = {0}; + // String str; + + // double tickVoltage = (double) threshold; - g.drawLine(0, tickVoltage, s.nSamples, tickVoltage); + // // If the limits are bad we don't want to hang the program trying to draw too many ticks + // // so count the number of ticks drawn and kill the routine after 100 draws + // int tickCount=0; + // while (tickVoltage < ylims[1] - voltPerTick*1.5) // Draw the ticks above the thold line + // { + // tickVoltage = (double) roundUp(tickVoltage + voltPerTick, 100); - // glBegin(GL_LINE_STRIP); - // glVertex2i(0, tickVoltage); - // glVertex2i(s.nSamples, tickVoltage); - // glEnd(); + // g.drawLine(0, tickVoltage, s.nSamples, tickVoltage); - makeLabel(tickVoltage, gain, true, cstr); - str = String(cstr); - g.setFont(font); - g.drawText(str, 1, tickVoltage+voltPerTick/10, 100, 15, Justification::left, false); + // // glBegin(GL_LINE_STRIP); + // // glVertex2i(0, tickVoltage); + // // glVertex2i(s.nSamples, tickVoltage); + // // glEnd(); - if (tickCount++>100) - return; + // makeLabel(tickVoltage, gain, true, cstr); + // str = String(cstr); + // g.setFont(font); + // g.drawText(str, 1, tickVoltage+voltPerTick/10, 100, 15, Justification::left, false); + + // if (tickCount++>100) + // return; + // } + + // tickVoltage = threshold; + // tickCount = 0; + + // while (tickVoltage > ylims[0] + voltPerTick) // draw the ticks below the thold line + // { + // tickVoltage = (double) roundUp(tickVoltage - voltPerTick, 100); + + // g.drawLine(0, tickVoltage, s.nSamples, tickVoltage); + + // // glBegin(GL_LINE_STRIP); + // // glVertex2i(0, tickVoltage); + // // glVertex2i(s.nSamples, tickVoltage); + // // glEnd(); + + // makeLabel(tickVoltage, gain, true, cstr); + // str = String(cstr); + // g.drawText(str, 1, tickVoltage+voltPerTick/10, 100, 15, Justification::left, false); + + // if (tickCount++>100) + // return; + // } +} + +void WaveAxes::updateSpikeData(const SpikeObject& s) +{ + if (!gotFirstSpike) + { + gotFirstSpike = true; } - tickVoltage = threshold; - tickCount = 0; + SpikeObject newSpike = s; - while (tickVoltage > ylims[0] + voltPerTick) // draw the ticks below the thold line + spikeBuffer.set(spikeIndex, newSpike); + + spikeIndex++; + spikeIndex %= bufferSize; + +} + +void WaveAxes::clear() +{ + +} + +void WaveAxes::mouseMove(const MouseEvent& event) +{ + + // Point<int> pos = event.getPosition(); + + float y = event.y; + + float h = getHeight()*thresholdLevel; + + // std::cout << y << " " << h << std::endl; + + if (y > h - 10.0f && y < h + 10.0f && !isOverThresholdSlider) { - tickVoltage = (double) roundUp(tickVoltage - voltPerTick, 100); + thresholdColour = Colours::yellow; + + // std::cout << "Yes." << std::endl; + + repaint(); + + isOverThresholdSlider = true; + + // cursorType = MouseCursor::DraggingHandCursor; - g.drawLine(0, tickVoltage, s.nSamples, tickVoltage); + } else if ((y < h - 10.0f || y > h + 10.0f) && isOverThresholdSlider){ - // glBegin(GL_LINE_STRIP); - // glVertex2i(0, tickVoltage); - // glVertex2i(s.nSamples, tickVoltage); - // glEnd(); + thresholdColour = Colours::red; + repaint(); - makeLabel(tickVoltage, gain, true, cstr); - str = String(cstr); - g.drawText(str, 1, tickVoltage+voltPerTick/10, 100, 15, Justification::left, false); + isOverThresholdSlider = false; - if (tickCount++>100) - return; + // cursorType = MouseCursor::NormalCursor; + } + + } -void WaveAxes::clear() +void WaveAxes::mouseDown(const MouseEvent& event) +{ + // if (isOverThresholdSlider) + // { + // cursorType = MouseCursor::DraggingHandCursor; + // } +} + +void WaveAxes::mouseDrag(const MouseEvent& event) +{ + if (isOverThresholdSlider) + { + thresholdLevel = float(event.y) / float(getHeight()); + repaint(); + } +} + +MouseCursor WaveAxes::getMouseCursor() { + MouseCursor c = MouseCursor(cursorType); + return c; } +void WaveAxes::mouseExit(const MouseEvent& event) +{ + if (isOverThresholdSlider) + { + isOverThresholdSlider = false; + thresholdColour = Colours::red; + repaint(); + } +} // -------------------------------------------------- -ProjectionAxes::ProjectionAxes(int projectionNum) : GenericAxes(projectionNum) +ProjectionAxes::ProjectionAxes(int projectionNum) : GenericAxes(projectionNum), imageDim(500) { + projectionImage = Image(Image::RGB, imageDim, imageDim, true); + + clear(); + //Graphics g(projectionImage); + //g.setColour(Colours::red); + //g.fillEllipse(20, 20, 300, 200); + + n2ProjIdx(projectionNum, &Dim1, &Dim2); + } void ProjectionAxes::paint(Graphics& g) { - g.setColour(Colours::orange); - g.fillRect(5,5,getWidth()-5, getHeight()-5); + //g.setColour(Colours::orange); + //g.fillRect(5,5,getWidth()-5, getHeight()-5); + g.drawImage(projectionImage, + 5, 5, getWidth()-10, getHeight()-10, + 0, 250, 250, 250); +} + +void ProjectionAxes::updateSpikeData(const SpikeObject& s) +{ + if (!gotFirstSpike) + { + gotFirstSpike = true; + } + + int idx1, idx2; + calcWaveformPeakIdx(s, ampDim1, ampDim2, &idx1, &idx2); + + // add peaks to image + updateProjectionImage(s.data[idx1], s.data[idx2]); + +} + +void ProjectionAxes::updateProjectionImage(uint16_t x, uint16_t y) +{ + Graphics g(projectionImage); + + float xf = float(x-32768)*(float(imageDim)/32768.0); + float yf = float(imageDim) - float(y-32768)*(float(imageDim)/32768.0); + + g.setColour(Colours::white); + g.fillEllipse(xf,yf,2.0f,2.0f); + +} + +void ProjectionAxes::calcWaveformPeakIdx(const SpikeObject& s, int d1, int d2, int* idx1, int* idx2) +{ + + int max1 = -1*pow(2.0,15); + int max2 = max1; + + for (int i = 0; i < s.nSamples; i++) + { + if (s.data[d1*s.nSamples + i] > max1) + { + *idx1 = d1*s.nSamples+i; + max1 = s.data[*idx1]; + } + if (s.data[d2*s.nSamples+i] > max2) + { + *idx2 = d2*s.nSamples+i; + max2 = s.data[*idx2]; + } + } } + + void ProjectionAxes::clear() { + projectionImage.clear(Rectangle<int>(0, 0, projectionImage.getWidth(), projectionImage.getHeight()), + Colours::black); +} +void ProjectionAxes::n2ProjIdx(int proj, int* p1, int* p2) +{ + int d1, d2; + if (proj==PROJ1x2) + { + d1 = 0; + d2 = 1; + } + else if (proj==PROJ1x3) + { + d1 = 0; + d2 = 2; + } + else if (proj==PROJ1x4) + { + d1 = 0; + d2 = 3; + } + else if (proj==PROJ2x3) + { + d1 = 1; + d2 = 2; + } + else if (proj==PROJ2x4) + { + d1 = 1; + d2 = 3; + } + else if (proj==PROJ3x4) + { + d1 = 2; + d2 = 3; + } + else + { + std::cout<<"Invalid projection:"<<proj<<"! Cannot determine d1 and d2"<<std::endl; + *p1 = -1; + *p2 = -1; + return; + } + *p1 = d1; + *p2 = d2; } // -------------------------------------------------- @@ -848,7 +1064,9 @@ void GenericAxes::makeLabel(int val, int gain, bool convert, char* s) sprintf(s, "%.2fuV", volt); } else + { sprintf(s,"%d", (int)val); + } } double GenericAxes::ad16ToUv(int x, int gain) diff --git a/Source/Processors/Visualization/SpikeDisplayCanvas.h b/Source/Processors/Visualization/SpikeDisplayCanvas.h index 8c3ede25520095e869ffe33a81af860aa4b77a2d..008703c2a6aaa18bb4149a464f67aaad288dc1a7 100755 --- a/Source/Processors/Visualization/SpikeDisplayCanvas.h +++ b/Source/Processors/Visualization/SpikeDisplayCanvas.h @@ -206,7 +206,6 @@ private: void setLimitsOnAxes(); void updateAxesPositions(); - void n2ProjIdx(int i, int* p1, int* p2); Font font; @@ -228,7 +227,7 @@ public: virtual ~GenericAxes(); - void updateSpikeData(const SpikeObject& s); + virtual void updateSpikeData(const SpikeObject& s); void setXLims(double xmin, double xmax); void getXLims(double* xmin, double* xmax); @@ -260,7 +259,6 @@ protected: }; - /** Class for drawing spike waveforms. @@ -273,10 +271,21 @@ public: WaveAxes(int channel); ~WaveAxes() {} + void updateSpikeData(const SpikeObject& s); + void paint(Graphics& g); + void plotSpike(const SpikeObject& s, Graphics& g); + void clear(); + void mouseMove(const MouseEvent& event); + void mouseExit(const MouseEvent& event); + void mouseDown(const MouseEvent& event); + void mouseDrag(const MouseEvent& event); + + MouseCursor getMouseCursor(); + private: Colour waveColour; @@ -285,14 +294,30 @@ private: bool drawGrid; + float thresholdLevel; + void drawWaveformGrid(int threshold, int gain, Graphics& g); + void drawThresholdSlider(Graphics& g); + Font font; + Array<SpikeObject> spikeBuffer; + + int spikeIndex; + int bufferSize; + + bool isOverThresholdSlider; + bool isDraggingThresholdSlider; + + MouseCursor::StandardCursorType cursorType; + }; + + /** Class for drawing the peak projections of spike waveforms. @@ -305,15 +330,30 @@ public: ProjectionAxes(int projectionNum); ~ProjectionAxes() {} + void updateSpikeData(const SpikeObject& s); + void paint(Graphics& g); void clear(); private: + void updateProjectionImage(uint16_t, uint16_t); + + void calcWaveformPeakIdx(const SpikeObject&, int, int, int*, int*); + + + void n2ProjIdx(int i, int* p1, int* p2); + + int ampDim1, ampDim2; + + Image projectionImage; + Colour pointColour; Colour gridColour; + int imageDim; + }; diff --git a/Source/UI/UIComponent.cpp b/Source/UI/UIComponent.cpp index 015d375e5b73c5557df08fad8521e08071c7cf93..34747cb8248b924195079093dcd8ad199231c08f 100755 --- a/Source/UI/UIComponent.cpp +++ b/Source/UI/UIComponent.cpp @@ -92,6 +92,8 @@ UIComponent::UIComponent(MainWindow* mainWindow_, ProcessorGraph* pgraph, AudioC mainWindow->setMenuBar(this); #endif + // getEditorViewport()->loadState(File("/home/jsiegle/Programming/GUI/Builds/Linux/build/spike_display.xml")); + } UIComponent::~UIComponent()