Skip to content
Snippets Groups Projects
Commit 470b3172 authored by jsiegle's avatar jsiegle
Browse files

Allow SpikeDisplayCanvas to draw multiple waveforms and projection plots

parent 63785adc
No related branches found
No related tags found
No related merge requests found
...@@ -238,7 +238,7 @@ public: ...@@ -238,7 +238,7 @@ public:
virtual void loadEditorParameters(XmlElement* xml); virtual void loadEditorParameters(XmlElement* xml);
/** Syncs parametereditor colors with parameter values */ /** Syncs parametereditor colors with parameter values */
void updateParameterButtons(int parameterIndex); void updateParameterButtons(int parameterIndex = -1);
protected: protected:
/** A pointer to the button that opens the drawer for the ChannelSelector. */ /** A pointer to the button that opens the drawer for the ChannelSelector. */
......
...@@ -63,3 +63,4 @@ void LfpDisplayEditor::buttonCallback(Button* button) ...@@ -63,3 +63,4 @@ void LfpDisplayEditor::buttonCallback(Button* button)
} }
} }
...@@ -463,18 +463,18 @@ void SpikePlot::setLimitsOnAxes() ...@@ -463,18 +463,18 @@ void SpikePlot::setLimitsOnAxes()
{ {
//std::cout<<"SpikePlot::setLimitsOnAxes()"<<std::endl; //std::cout<<"SpikePlot::setLimitsOnAxes()"<<std::endl;
for (int i = 0; i < nWaveAx; i++) // for (int i = 0; i < nWaveAx; i++)
wAxes[i]->setYLims(limits[i][0], limits[i][1]); // wAxes[i]->setYLims(limits[i][0], limits[i][1]);
// Each Projection sets its limits using the limits of the two waveform dims it represents. // // 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 // // Convert projection number to indecies, and then set the limits using those indices
int j1, j2; // int j1, j2;
for (int i = 0; i < nProjAx; i++) // for (int i = 0; i < nProjAx; i++)
{ // {
n2ProjIdx(pAxes[i]->getType(), &j1, &j2); // n2ProjIdx(pAxes[i]->getType(), &j1, &j2);
pAxes[i]->setYLims(limits[j1][0], limits[j1][1]); // pAxes[i]->setYLims(limits[j1][0], limits[j1][1]);
pAxes[i]->setXLims(limits[j2][0], limits[j2][1]); // pAxes[i]->setXLims(limits[j2][0], limits[j2][1]);
} // }
} }
void SpikePlot::initLimits() void SpikePlot::initLimits()
...@@ -558,56 +558,23 @@ void SpikePlot::zoom(int dim, bool in) ...@@ -558,56 +558,23 @@ 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)
{ {
font = Font("Small Text",10,Font::plain); 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) void WaveAxes::paint(Graphics& g)
...@@ -617,109 +584,163 @@ void WaveAxes::paint(Graphics& g) ...@@ -617,109 +584,163 @@ void WaveAxes::paint(Graphics& g)
int chan = 0; int chan = 0;
// draw the grid lines for the waveforms
if (drawGrid)
drawWaveformGrid(s.threshold[chan], s.gain[chan], g);
// if no spikes have been received then don't plot anything // if no spikes have been received then don't plot anything
if (!gotFirstSpike) if (!gotFirstSpike)
{ {
return; 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);
// draw the threshold line and labels
// TODO
}
void WaveAxes::plotSpike(const SpikeObject& s, Graphics& g)
{
float h = getHeight(); 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 //compute the spatial width for each waveform sample
float dx = (getWidth()-10)/s.nSamples; float dx = (getWidth()-10)/float(spikeBuffer[0].nSamples);
float x = 5.0f;
// type corresponds to channel so we need to calculate the starting // type corresponds to channel so we need to calculate the starting
// sample based upon which channel is getting plotted // sample based upon which channel is getting plotted
int sampIdx = s.nSamples * type; // int sampIdx = 40*type; //spikeBuffer[0].nSamples * type; //
// draw the individual waveform points
g.setColour(Colours::white);
int dSamples = 1; 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; //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; sampIdx += dSamples;
x += dx; x += dx;
} }
// draw the threshold line and labels
} }
void WaveAxes::drawWaveformGrid(int threshold, int gain, Graphics& g) 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; float h = getHeight();
while (nTicks > MAX_N_TICKS) float w = getWidth();
for (int i = 1; i < 10; i++)
{ {
minPixelsPerTick += 5; g.setColour(Colours::darkgrey);
nTicks = pixelRange / minPixelsPerTick;
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); // int nTicks = pixelRange / minPixelsPerTick;
char cstr[200] = {0}; // while (nTicks > MAX_N_TICKS)
String str; // {
// 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 // g.setColour(Colours::red);
// so count the number of ticks drawn and kill the routine after 100 draws // char cstr[200] = {0};
int tickCount=0; // String str;
while (tickVoltage < ylims[1] - voltPerTick*1.5) // Draw the ticks above the thold line
{
tickVoltage = (double) roundUp(tickVoltage + voltPerTick, 100);
g.drawLine(0, tickVoltage, s.nSamples, tickVoltage); // double tickVoltage = (double) threshold;
// glBegin(GL_LINE_STRIP); // // If the limits are bad we don't want to hang the program trying to draw too many ticks
// glVertex2i(0, tickVoltage); // // so count the number of ticks drawn and kill the routine after 100 draws
// glVertex2i(s.nSamples, tickVoltage); // int tickCount=0;
// glEnd(); // while (tickVoltage < ylims[1] - voltPerTick*1.5) // Draw the ticks above the thold line
// {
// tickVoltage = (double) roundUp(tickVoltage + voltPerTick, 100);
makeLabel(tickVoltage, gain, true, cstr); // g.drawLine(0, tickVoltage, s.nSamples, tickVoltage);
str = String(cstr);
g.setFont(font);
g.drawText(str, 1, tickVoltage+voltPerTick/10, 100, 15, Justification::left, false);
if (tickCount++>100) // // glBegin(GL_LINE_STRIP);
return; // // glVertex2i(0, tickVoltage);
} // // glVertex2i(s.nSamples, tickVoltage);
// // glEnd();
tickVoltage = threshold; // makeLabel(tickVoltage, gain, true, cstr);
tickCount = 0; // str = String(cstr);
// g.setFont(font);
// g.drawText(str, 1, tickVoltage+voltPerTick/10, 100, 15, Justification::left, false);
while (tickVoltage > ylims[0] + voltPerTick) // draw the ticks below the thold line // if (tickCount++>100)
{ // return;
tickVoltage = (double) roundUp(tickVoltage - voltPerTick, 100); // }
g.drawLine(0, tickVoltage, s.nSamples, tickVoltage); // tickVoltage = threshold;
// tickCount = 0;
// glBegin(GL_LINE_STRIP); // while (tickVoltage > ylims[0] + voltPerTick) // draw the ticks below the thold line
// glVertex2i(0, tickVoltage); // {
// glVertex2i(s.nSamples, tickVoltage); // tickVoltage = (double) roundUp(tickVoltage - voltPerTick, 100);
// glEnd();
makeLabel(tickVoltage, gain, true, cstr); // g.drawLine(0, tickVoltage, s.nSamples, tickVoltage);
str = String(cstr);
g.drawText(str, 1, tickVoltage+voltPerTick/10, 100, 15, Justification::left, false);
if (tickCount++>100) // // glBegin(GL_LINE_STRIP);
return; // // 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;
} }
SpikeObject newSpike = s;
spikeBuffer.set(spikeIndex, newSpike);
spikeIndex++;
spikeIndex %= bufferSize;
} }
void WaveAxes::clear() void WaveAxes::clear()
...@@ -730,20 +751,126 @@ void WaveAxes::clear() ...@@ -730,20 +751,126 @@ void WaveAxes::clear()
// -------------------------------------------------- // --------------------------------------------------
ProjectionAxes::ProjectionAxes(int projectionNum) : GenericAxes(projectionNum) ProjectionAxes::ProjectionAxes(int projectionNum) : GenericAxes(projectionNum), imageDim(500)
{ {
projectionImage = Image(Image::RGB, imageDim, imageDim, true);
//Graphics g(projectionImage);
//g.setColour(Colours::red);
//g.fillEllipse(20, 20, 300, 200);
n2ProjIdx(projectionNum, &ampDim1, &ampDim2);
} }
void ProjectionAxes::paint(Graphics& g) void ProjectionAxes::paint(Graphics& g)
{ {
g.setColour(Colours::orange); //g.setColour(Colours::orange);
g.fillRect(5,5,getWidth()-5, getHeight()-5); //g.fillRect(5,5,getWidth()-5, getHeight()-5);
g.drawImage(projectionImage,
5, 5, getWidth()-10, getHeight()-10,
0, 0, projectionImage.getWidth(), projectionImage.getHeight());
}
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,5.0f,5.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() 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 +975,9 @@ void GenericAxes::makeLabel(int val, int gain, bool convert, char* s) ...@@ -848,7 +975,9 @@ void GenericAxes::makeLabel(int val, int gain, bool convert, char* s)
sprintf(s, "%.2fuV", volt); sprintf(s, "%.2fuV", volt);
} }
else else
{
sprintf(s,"%d", (int)val); sprintf(s,"%d", (int)val);
}
} }
double GenericAxes::ad16ToUv(int x, int gain) double GenericAxes::ad16ToUv(int x, int gain)
......
...@@ -206,7 +206,6 @@ private: ...@@ -206,7 +206,6 @@ private:
void setLimitsOnAxes(); void setLimitsOnAxes();
void updateAxesPositions(); void updateAxesPositions();
void n2ProjIdx(int i, int* p1, int* p2);
Font font; Font font;
...@@ -228,7 +227,7 @@ public: ...@@ -228,7 +227,7 @@ public:
virtual ~GenericAxes(); virtual ~GenericAxes();
void updateSpikeData(const SpikeObject& s); virtual void updateSpikeData(const SpikeObject& s);
void setXLims(double xmin, double xmax); void setXLims(double xmin, double xmax);
void getXLims(double* xmin, double* xmax); void getXLims(double* xmin, double* xmax);
...@@ -273,8 +272,12 @@ public: ...@@ -273,8 +272,12 @@ public:
WaveAxes(int channel); WaveAxes(int channel);
~WaveAxes() {} ~WaveAxes() {}
void updateSpikeData(const SpikeObject& s);
void paint(Graphics& g); void paint(Graphics& g);
void plotSpike(const SpikeObject& s, Graphics& g);
void clear(); void clear();
private: private:
...@@ -289,6 +292,11 @@ private: ...@@ -289,6 +292,11 @@ private:
Font font; Font font;
Array<SpikeObject> spikeBuffer;
int spikeIndex;
int bufferSize;
}; };
...@@ -305,15 +313,30 @@ public: ...@@ -305,15 +313,30 @@ public:
ProjectionAxes(int projectionNum); ProjectionAxes(int projectionNum);
~ProjectionAxes() {} ~ProjectionAxes() {}
void updateSpikeData(const SpikeObject& s);
void paint(Graphics& g); void paint(Graphics& g);
void clear(); void clear();
private: 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 pointColour;
Colour gridColour; Colour gridColour;
int imageDim;
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment