Skip to content
Snippets Groups Projects
Commit 32af7a13 authored by jvoigts's avatar jvoigts
Browse files

LFP redraws are now incremental

LFP traces are now only drawn in the space since the last buffer update, this reduced cpu load.
Reverted back to the linedraw method because speed is not an issue any more with this fix.
Currently not issuing full redraws when a user uses the scrollbar, only for clicks and mouse wheel messages.
parent 7050830f
No related branches found
No related tags found
No related merge requests found
......@@ -52,6 +52,11 @@ LfpDisplayCanvas::LfpDisplayCanvas(LfpDisplayNode* processor_) :
scrollBarThickness = viewport->getScrollBarThickness();
//viewport->getVerticalScrollBar()->addListener(this->scrollBarMoved(viewport->getVerticalScrollBar(), 1.0));
addAndMakeVisible(viewport);
addAndMakeVisible(timescale);
......@@ -112,7 +117,7 @@ LfpDisplayCanvas::~LfpDisplayCanvas()
void LfpDisplayCanvas::resized()
{
timescale->setBounds(0,0,getWidth()-scrollBarThickness,30);
timescale->setBounds(leftmargin,0,getWidth()-scrollBarThickness,30);
viewport->setBounds(0,30,getWidth(),getHeight()-90);
lfpDisplay->setBounds(0,0,getWidth()-scrollBarThickness, getChannelHeight()*nChans);
......@@ -157,7 +162,7 @@ void LfpDisplayCanvas::update()
lfpDisplay->setBounds(0,0,getWidth()-scrollBarThickness*2, lfpDisplay->getTotalHeight());
repaint();
//repaint();
}
......@@ -185,6 +190,9 @@ void LfpDisplayCanvas::comboBoxChanged(ComboBox* cb)
timescale->setTimebase(timebase);
}
int LfpDisplayCanvas::getChannelHeight()
{
return spreads[spreadSelection->getSelectedId()-1].getIntValue();
......@@ -240,15 +248,20 @@ void LfpDisplayCanvas::refreshScreenBuffer()
void LfpDisplayCanvas::updateScreenBuffer()
{
// copy new samples from the displayBuffer into the screenBuffer (waves)
int maxSamples = lfpDisplay->getWidth();
if (screenBufferIndex>=maxSamples-1) // wrap around if we reached right edge before
screenBufferIndex=leftmargin;
lastScreenBufferIndex = screenBufferIndex;
int maxSamples = lfpDisplay->getWidth();
int index = processor->getDisplayBufferIndex();
int nSamples = index - displayBufferIndex;
int nSamples = index - displayBufferIndex; // N new samples to be addeddisplayBufferIndex
if (nSamples < 0) // buffer has reset to 0
{
......@@ -257,16 +270,22 @@ void LfpDisplayCanvas::updateScreenBuffer()
float ratio = sampleRate * timebase / float(getWidth());
// this number is crucial:
// this number is crucial: converting from samples to values (in px) for the screen buffer
int valuesNeeded = (int) float(nSamples) / ratio;
if ( screenBufferIndex + valuesNeeded > maxSamples) // crop number of samples to fit cavas width
{
valuesNeeded = maxSamples - screenBufferIndex ;
}
float subSampleOffset = 0.0;
int nextPos = (displayBufferIndex + 1) % displayBufferSize;
int nextPos = (displayBufferIndex + 0) % displayBufferSize; // position next to displayBufferIndex in display buffer to copy from
if (valuesNeeded > 0 && valuesNeeded < 1000)
{
for (int i = 0; i < valuesNeeded; i++)
for (int i = 0; i < valuesNeeded +0; i++) // also fill one extra sample for line drawing interpolation to match across draws
{
float gain = 1.0;
float alpha = (float) subSampleOffset;
......@@ -274,6 +293,7 @@ void LfpDisplayCanvas::updateScreenBuffer()
screenBuffer->clear(screenBufferIndex, 1);
if (displayBufferIndex<=index && nextPos<=index){
for (int channel = 0; channel < nChans; channel++)
{
......@@ -297,8 +317,8 @@ void LfpDisplayCanvas::updateScreenBuffer()
//waves[channel][screenBufferIndex*2+1] += 0.5f; // to center in viewport
}
};
};
//// now do the event channel
//// waves[nChans][screenBufferIndex*2+1] =
// *(displayBuffer->getSampleData(nChans, displayBufferIndex));
......@@ -308,7 +328,7 @@ void LfpDisplayCanvas::updateScreenBuffer()
while (subSampleOffset >= 1.0)
{
if (++displayBufferIndex >= displayBufferSize)
if (++displayBufferIndex > displayBufferSize)
displayBufferIndex = 0;
nextPos = (displayBufferIndex + 1) % displayBufferSize;
......@@ -319,6 +339,17 @@ void LfpDisplayCanvas::updateScreenBuffer()
screenBufferIndex %= maxSamples;
}
// to deal with screenbufferindex wrapping:
// if we just hit the right side of the lfp canvas, then lastScreenBufferIndex is to the right and screenBufferIndex is 0 (wrapped around because of the modulo above)
// in this case, we need to set screenBufferIndex to the right edge
if (screenBufferIndex == 0 && lastScreenBufferIndex > screenBufferIndex)
{
screenBufferIndex=maxSamples; // draw last slice on the right side just up to the end of the canvas
//std::cout << " wrap "<< std::endl;
}
//std::cout << " "<< lastScreenBufferIndex<< " - "<< screenBufferIndex<< std::endl;
}
else
......@@ -341,8 +372,7 @@ void LfpDisplayCanvas::paint(Graphics& g)
{
//std::cout << "Painting" << std::endl;
g.setColour(Colour(25,25,25));
g.setColour(Colour(5,5,5));
g.fillRect(0, 0, getWidth(), getHeight());
g.setColour(Colour(40,40,40));
......@@ -364,17 +394,16 @@ void LfpDisplayCanvas::paint(Graphics& g)
g.setColour(Colour(100,100,100));
g.drawText("Voltage range (uV)",5,getHeight()-55,300,20,Justification::left, false);
g.drawText("Timebase (s)",175,getHeight()-55,300,20,Justification::left, false);
g.drawText("Spread (px)",345,getHeight()-55,300,20,Justification::left, false);
}
void LfpDisplayCanvas::refresh()
void LfpDisplayCanvas::refresh()
{
updateScreenBuffer();
lfpDisplay->refresh();
lfpDisplay->refresh(); // redraws only the new part of the screen buffer
//getPeer()->performAnyPendingRepaintsNow();
......@@ -485,14 +514,9 @@ LfpDisplay::LfpDisplay(LfpDisplayCanvas* c, Viewport* v) :
addMouseListener(this, true);
for (int i = 0; i < 10; i++)
{
channelColours.add(Colour(200,200,255-i*25));
}
for (int i = 10; i > -1; i--)
for (int i = 0; i < 15; i++)
{
channelColours.add(Colour(200,200,255-i*25));
channelColours.add(Colour(float(sin((3.14/2)*(float(i)/15))),float(1.0),float(1),float(1.0)));
}
}
......@@ -557,6 +581,8 @@ void LfpDisplay::resized()
}
canvas->fullredraw=true;//issue full redraw
// std::cout << "Total height: " << totalHeight << std::endl;
}
......@@ -582,13 +608,18 @@ void LfpDisplay::refresh()
if ((topBorder <= componentBottom && bottomBorder >= componentTop))
{
getChildComponent(i)->repaint();
if (canvas->fullredraw){
channels[i]->fullredraw=true;
getChildComponent(i)->repaint();
} else{
getChildComponent(i)->repaint( canvas->lastScreenBufferIndex-2, 0, (canvas->screenBufferIndex-canvas->lastScreenBufferIndex)+3, getChildComponent(i)->getHeight() ); //repaint only the updated portion
// we redraw from -2 to +1 relative to the real redraw window, the -3 makes sure that the lines join nicely, and the +1 draws the vertical update line
}
//std::cout << i << std::endl;
}
}
canvas->fullredraw=false;
}
void LfpDisplay::setRange(float r)
......@@ -617,6 +648,17 @@ void LfpDisplay::setChannelHeight(int r)
}
void LfpDisplay::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel ) {
canvas->fullredraw=true;//issue full redraw
// 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);
}
void LfpDisplay::mouseDown(const MouseEvent& event)
{
//int x = event.getMouseDownX();
......@@ -634,7 +676,9 @@ void LfpDisplay::mouseDown(const MouseEvent& event)
lcd->select();
repaint();
canvas->fullredraw=true;//issue full redraw
//repaint();
}
......@@ -662,14 +706,17 @@ void LfpChannelDisplay::paint(Graphics& g)
//g.fillAll(Colours::grey);
g.setColour(Colours::yellow);
g.setColour(Colours::yellow); // draw most recent drawn sample position
g.drawLine(canvas->screenBufferIndex+1, 0, canvas->screenBufferIndex+1, getHeight()-channelOverlap);
g.drawLine(canvas->screenBufferIndex, 0, canvas->screenBufferIndex, getHeight()-channelOverlap);
//g.setColour(Colours::red); // draw oldest drawn sample position
//g.drawLine(canvas->lastScreenBufferIndex, 0, canvas->lastScreenBufferIndex, getHeight()-channelOverlap);
int center = getHeight()/2;
if (isSelected)
{
g.setColour(Colours::lightgrey);
g.fillRect(0,center-channelHeight/2,10,channelHeight);
g.drawLine(0,center+channelHeight/2,getWidth(),center+channelHeight/2);
......@@ -686,56 +733,69 @@ void LfpChannelDisplay::paint(Graphics& g)
g.drawLine(0, getHeight()/2, getWidth(), getHeight()/2);
int stepSize = 1;
int from = 0;
int from = 0; // for vertical line drawing in the LFP data
int to = 0;
g.setColour(lineColour);
for (int i = 0; i < getWidth()-stepSize; i += stepSize)
{
//for (int i = 0; i < getWidth()-stepSize; i += stepSize) // redraw entire display
int ifrom=canvas->lastScreenBufferIndex-3; // need to start drawing a bit before the actual redraw windowfor the interpolated line to join correctly
if (ifrom<0) ifrom=0;
int ito = canvas->screenBufferIndex-1;
// drawLine makes for nice anti-aliased plots, but is pretty slow
// g.drawLine(i,
// (canvas->getYCoord(chan, i)/range*channelHeightFloat)+getHeight()/2,
// i+stepSize,
// (canvas->getYCoord(chan, i+stepSize)/range*channelHeightFloat)+getHeight()/2);
if (fullredraw){
ifrom=canvas->leftmargin;
ito=getWidth()-stepSize;
fullredraw=false;
}
for (int i = ifrom; i < ito ; i += stepSize) // redraw only changed portion
{
// // pixel wise line plot has no anti-aliasing, but runs much faster
double a = (canvas->getYCoord(chan, i)/range*channelHeightFloat)+getHeight()/2;
double b = (canvas->getYCoord(chan, i+stepSize)/range*channelHeightFloat)+getHeight()/2;
// drawLine makes for ok anti-aliased plots, but is pretty slow
g.drawLine(i,
(canvas->getYCoord(chan, i)/range*channelHeightFloat)+getHeight()/2,
i+stepSize,
(canvas->getYCoord(chan, i+stepSize)/range*channelHeightFloat)+getHeight()/2);
if (a<b){
from = (a);
to = (b);
} else {
from = (b);
to = (a);
}
if ((to-from) < 40){ // if there is too much vertical range in one pixel, don't draw the full line for speed reasons
for (int j = from; j <= to; j += 1)
{
g.setPixel(i,j);
}
} else if ((to-from) < 100){
for (int j = from; j <= to; j += 2)
{
g.setPixel(i,j);
}
} else {
g.setPixel(i,to);
g.setPixel(i,from);
}
if (false) // switched back to line drawing now that we only draw partial updates
{
// // pixel wise line plot has no anti-aliasing, but runs much faster
double a = (canvas->getYCoord(chan, i)/range*channelHeightFloat)+getHeight()/2;
double b = (canvas->getYCoord(chan, i+stepSize)/range*channelHeightFloat)+getHeight()/2;
if (a<b){
from = (a);
to = (b);
} else {
from = (b);
to = (a);
}
if ((to-from) < 40){ // if there is too much vertical range in one pixel, don't draw the full line for speed reasons
for (int j = from; j <= to; j += 1)
{
g.setPixel(i,j);
}
} else if ((to-from) < 100){
for (int j = from; j <= to; j += 2)
{
g.setPixel(i,j);
}
} else {
g.setPixel(i,to);
g.setPixel(i,from);
}
}
}
// g.setColour(lineColour.withAlpha(0.7f)); // alpha on seems to decrease draw speed
g.setFont(channelFont);
g.setFont(channelHeightFloat);
g.setFont(channelHeightFloat*0.6);
g.drawText(String(chan+1), 15, center-channelHeight/2, 200, channelHeight, Justification::left, false);
g.drawText(String(chan+1), 10, center-channelHeight/2, 200, channelHeight, Justification::left, false);
}
......
......@@ -43,7 +43,7 @@ class LfpChannelDisplay;
class LfpDisplayCanvas : public Visualizer,
public ComboBox::Listener
{
public:
LfpDisplayCanvas(LfpDisplayNode* n);
......@@ -79,7 +79,10 @@ public:
void loadVisualizerParameters(XmlElement* xml);
//void scrollBarMoved(ScrollBar *scrollBarThatHasMoved, double newRangeStart);
bool fullredraw; // used to indicate that a full redraw is required. is set false after each full redraw, there is a similar switch for ach ch display;
static const int leftmargin=50; // left margin for lfp plots (so the ch number text doesnt overlap)
private:
......@@ -93,6 +96,9 @@ private:
static const int MAX_N_SAMP = 5000; // maximum display size in pixels
//float waves[MAX_N_CHAN][MAX_N_SAMP*2]; // we need an x and y point for each sample
LfpDisplayNode* processor;
AudioSampleBuffer* displayBuffer;
AudioSampleBuffer* screenBuffer;
......@@ -120,6 +126,7 @@ private:
int nChans;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LfpDisplayCanvas);
};
......@@ -163,6 +170,7 @@ public:
void resized();
void mouseDown(const MouseEvent& event);
void mouseWheelMove(const MouseEvent& event, const MouseWheelDetails& wheel ) ;
void setRange(float range);
void setChannelHeight(int r);
......@@ -203,6 +211,8 @@ public:
void setRange(float range);
bool fullredraw; // used to indicate that a full redraw is required. is set false after each full redraw
private:
LfpDisplayCanvas* canvas;
......
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