diff --git a/Source/Processors/Editors/ChannelSelector.cpp b/Source/Processors/Editors/ChannelSelector.cpp index cc4d60069c0e66fcc0d710263a30c71ddcc7b818..77a6c74b2bff2adaaf0b2381916949e8da981868 100755 --- a/Source/Processors/Editors/ChannelSelector.cpp +++ b/Source/Processors/Editors/ChannelSelector.cpp @@ -138,19 +138,19 @@ void ChannelSelector::refreshButtonBoundaries() { parameterButtons[i]->setBounds(columnWidth/2 + offsetLR + columnWidth*((i)%nColumns), - floor((i)/nColumns)*rowHeight+offsetUD + topOffset, + floor(double(i)/nColumns)*rowHeight+offsetUD + topOffset, columnWidth, rowHeight); if (isNotSink) { recordButtons[i]->setBounds(columnWidth/2 + offsetLR + columnWidth*((i)%nColumns) - getDesiredWidth(), - floor((i)/nColumns)*rowHeight+offsetUD + topOffset, + floor(double(i)/nColumns)*rowHeight+offsetUD + topOffset, columnWidth, rowHeight); audioButtons[i]->setBounds(columnWidth/2 + offsetLR + columnWidth*((i)%nColumns) - getDesiredWidth()*2, - floor((i)/nColumns)*rowHeight+offsetUD + topOffset, + floor(double(i)/nColumns)*rowHeight+offsetUD + topOffset, columnWidth, rowHeight); } diff --git a/Source/Processors/Visualization/SpikeObject.cpp b/Source/Processors/Visualization/SpikeObject.cpp index c36fdac8ce40e4196c7e3c3e215ca1923c5e9279..2c5b44f07ec829b456bc2953d16a094e0a1f468e 100755 --- a/Source/Processors/Visualization/SpikeObject.cpp +++ b/Source/Processors/Visualization/SpikeObject.cpp @@ -1,242 +1,242 @@ -/* - ------------------------------------------------------------------ - - This file is part of the Open Ephys GUI - Copyright (C) 2012 Open Ephys - - ------------------------------------------------------------------ - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - -*/ - -#include "SpikeObject.h" -#include "memory.h" -#include <stdlib.h> -#include "time.h" - -// Simple method for serializing a SpikeObject into a string of bytes -int packSpike(SpikeObject *s, uint8_t* buffer, int bufferSize){ - - //int reqBytes = 1 + 4 + 2 + 2 + 2 + 2 * s->nChannels * s->nSamples + 2 * s->nChannels * 2; - - int idx = 0; - - s->eventType = SPIKE_EVENT_CODE; - - - memcpy(buffer+idx, &(s->eventType), 1); - idx += 1; - - memcpy(buffer+idx, &(s->timestamp), 4); - idx += 4; - - memcpy(buffer+idx, &(s->source), 2); - idx +=2; - - memcpy(buffer+idx, &(s->nChannels), 2); - idx +=2; - - memcpy(buffer+idx, &(s->nSamples), 2); - idx +=2; - - memcpy(buffer+idx, &(s->data), s->nChannels * s->nSamples * 2); - idx += s->nChannels * s->nSamples * 2; - - memcpy(buffer+idx, &(s->gain), s->nChannels * 2); - idx += s->nChannels * 2; - - memcpy(buffer+idx, &(s->threshold), s->nChannels * 2); - idx += s->nChannels * 2; - - - - if (idx >= MAX_SPIKE_BUFFER_LEN){ - std::cout<<"Spike is larger than it should be. Size was:"<<idx<<" Max Size is:"<<MAX_SPIKE_BUFFER_LEN<<std::endl; - - } - // makeBufferValid(buffer, bufferSize); - - return idx; - -} - -// Simple method for deserializing a string of bytes into a Spike object -bool unpackSpike(SpikeObject *s, uint8_t* buffer, int bufferSize){ - // if !(isBufferValid(buffer, bufferSize)); - // return false; - - int idx = 0; - - memcpy (&(s->eventType), buffer+idx, 1); - idx += 1; - - memcpy( &(s->timestamp), buffer+idx, 4); - idx += 4; - - memcpy( &(s->source), buffer+idx, 2); - idx += 2; - - memcpy( &(s->nChannels), buffer+idx, 2); - idx +=2; - - memcpy( &(s->nSamples), buffer+idx, 2); - idx +=2; - - memcpy( &(s->data), buffer+idx, s->nChannels * s->nSamples * 2); - idx += s->nChannels * s->nSamples * 2; - - memcpy( &(s->gain), buffer+idx, s->nChannels * 2); - idx += s->nChannels * 2; - - memcpy( &(s->threshold), buffer+idx, s->nChannels *2); - idx += s->nChannels * 2; - -// if (idx >= bufferSize) -// std::cout<<"Buffer Overrun! More data extracted than was given!"<<std::endl; - - return true; - - -} - -// Checks the validity of the buffer, this should be run before unpacking and after packing the buffer -bool isBufferValid(uint8_t *buffer, int bufferSize){ - - if (! CHECK_BUFFER_VALIDITY ) - return true; - - uint16_t runningSum = 0; - uint16_t value = 0; - - int idx = 0; - - for (idx; idx < bufferSize-2; idx += 2){ - memcpy(buffer + idx, &value, 2); - runningSum += value; - } - - uint16_t integrityCheck = 0; - memcpy(buffer + idx, &integrityCheck, 2); - - std::cout<<integrityCheck<<" == "<< runningSum <<std::endl; - - return (integrityCheck == runningSum); -} - -void makeBufferValid(uint8_t *buffer, int bufferSize){ - if (! CHECK_BUFFER_VALIDITY ) - return; - - uint16_t runningSum = 0; - uint16_t value = 0; - - int idx = 0; - - for (idx; idx < bufferSize-2; idx += 2){ - memcpy(buffer + idx, &value, 2); - runningSum += value; - } - - memcpy(&runningSum, buffer + idx, 2); - -} - -void generateSimulatedSpike(SpikeObject *s, uint64_t timestamp, int noise) -{ - //std::cout<<"generateSimulatedSpike()"<<std::endl; - - uint16_t trace[][32] = - { - { 880, 900, 940, 1040, 1290, 1790, 2475, 2995, 3110, 2890, - 2505, 2090, 1720, 1410, 1155, 945, 775, 635, 520, 420, - 340, 265, 205, 155, 115, 80, 50, 34, 10, 34, 50, 80}, - { 1040, 1090, 1190, 1350, 1600, 1960, 2380, 2790, 3080, 3140, - 2910, 2430, 1810, 1180, 680, 380, 270, 320, 460, 630, - 770, 870, 940, 970, 990, 1000, 1000, 1000, 1000, 1000, 1000, 1000}, - { 1000, 1000, 1000, 1000, 1000, 1040, 1140, 1440, 2040, 2240, - 2400, 2340, 2280, 1880, 1640, 920, 520, 300, 140, 040, - 20, 20, 40, 100, 260, 500, 740, 900, 960, 1000, 1000, 1000} - }; - - - // We don't want to shift the waveform but scale it, and we don't want to scale - // the baseline, just the peak of the waveform - float scale[32] = - { 1.0, 1.0, 1.0, 1.0, 1.1, 1.2, 1.3, 1.5, 1.7, 2.0, 2.1, 2.2, 2.1, 2.0, 1.7, 1.5, - 1.3, 1.2, 1.1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; - - uint16_t gain = 2000; - - s->eventType = SPIKE_EVENT_CODE; - s->timestamp = timestamp; - s->source = 0; - s->nChannels = 4; - s->nSamples = 32; - int idx=0; - - int waveType = rand()%2; // Pick one of the three predefined waveshapes to generate - int shift = 1000 + 32768; - - for (int i=0; i<4; i++) - { - s->gain[i] = gain; - s->threshold[i] = 4000; - double scaleExponent = (double)(rand()%26+2) / 10.0f; // Scale the wave between 50% and 150% - - for (int j=0; j<32; j++){ - - int n = 0; - if (noise>0){ - n = rand() % noise - noise/2; - } - - s->data[idx] = (trace[waveType][j] + n) * pow(scale[j],scaleExponent) + shift; - idx = idx+1; - } - } - -} -void generateEmptySpike(SpikeObject *s, int nChannels){ - - s->eventType = SPIKE_EVENT_CODE; - s->timestamp = 0; - s->source = 0; - s->nChannels = 4; - s->nSamples = 32; - - int idx = 0; - for (int i=0; i<4; i++) - { - s->gain[i] = 0; - s->threshold[i] = 0; - for (int j=0; j<32; j++){ - s->data[idx] = 0; - idx = idx+1; - } - } -} - -void printSpike(SpikeObject *s){ - - std::cout<< " SpikeObject:\n"; - std::cout<< "\tTimestamp:" << s->timestamp; - std::cout<< "\tSource:" << s->source; - std::cout<< "\tnChannels:" <<s->nChannels; - std::cout<<"\tnSamples" << s->nSamples; - std::cout<<"\n\t 8 Data Samples:"; - for (int i=0; i<8; i++) - std::cout<<s->data+i<<" "; - std::cout<<std::endl; -} +/* + ------------------------------------------------------------------ + + This file is part of the Open Ephys GUI + Copyright (C) 2012 Open Ephys + + ------------------------------------------------------------------ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "SpikeObject.h" +#include "memory.h" +#include <stdlib.h> +#include "time.h" + +// Simple method for serializing a SpikeObject into a string of bytes +int packSpike(SpikeObject *s, uint8_t* buffer, int bufferSize){ + + //int reqBytes = 1 + 4 + 2 + 2 + 2 + 2 * s->nChannels * s->nSamples + 2 * s->nChannels * 2; + + int idx = 0; + + s->eventType = SPIKE_EVENT_CODE; + + + memcpy(buffer+idx, &(s->eventType), 1); + idx += 1; + + memcpy(buffer+idx, &(s->timestamp), 4); + idx += 4; + + memcpy(buffer+idx, &(s->source), 2); + idx +=2; + + memcpy(buffer+idx, &(s->nChannels), 2); + idx +=2; + + memcpy(buffer+idx, &(s->nSamples), 2); + idx +=2; + + memcpy(buffer+idx, &(s->data), s->nChannels * s->nSamples * 2); + idx += s->nChannels * s->nSamples * 2; + + memcpy(buffer+idx, &(s->gain), s->nChannels * 2); + idx += s->nChannels * 2; + + memcpy(buffer+idx, &(s->threshold), s->nChannels * 2); + idx += s->nChannels * 2; + + + + if (idx >= MAX_SPIKE_BUFFER_LEN){ + std::cout<<"Spike is larger than it should be. Size was:"<<idx<<" Max Size is:"<<MAX_SPIKE_BUFFER_LEN<<std::endl; + + } + // makeBufferValid(buffer, bufferSize); + + return idx; + +} + +// Simple method for deserializing a string of bytes into a Spike object +bool unpackSpike(SpikeObject *s, uint8_t* buffer, int bufferSize){ + // if !(isBufferValid(buffer, bufferSize)); + // return false; + + int idx = 0; + + memcpy (&(s->eventType), buffer+idx, 1); + idx += 1; + + memcpy( &(s->timestamp), buffer+idx, 4); + idx += 4; + + memcpy( &(s->source), buffer+idx, 2); + idx += 2; + + memcpy( &(s->nChannels), buffer+idx, 2); + idx +=2; + + memcpy( &(s->nSamples), buffer+idx, 2); + idx +=2; + + memcpy( &(s->data), buffer+idx, s->nChannels * s->nSamples * 2); + idx += s->nChannels * s->nSamples * 2; + + memcpy( &(s->gain), buffer+idx, s->nChannels * 2); + idx += s->nChannels * 2; + + memcpy( &(s->threshold), buffer+idx, s->nChannels *2); + idx += s->nChannels * 2; + +// if (idx >= bufferSize) +// std::cout<<"Buffer Overrun! More data extracted than was given!"<<std::endl; + + return true; + + +} + +// Checks the validity of the buffer, this should be run before unpacking and after packing the buffer +bool isBufferValid(uint8_t *buffer, int bufferSize){ + + if (! CHECK_BUFFER_VALIDITY ) + return true; + + uint16_t runningSum = 0; + uint16_t value = 0; + + int idx = 0; + + for (idx; idx < bufferSize-2; idx += 2){ + memcpy(buffer + idx, &value, 2); + runningSum += value; + } + + uint16_t integrityCheck = 0; + memcpy(buffer + idx, &integrityCheck, 2); + + std::cout<<integrityCheck<<" == "<< runningSum <<std::endl; + + return (integrityCheck == runningSum); +} + +void makeBufferValid(uint8_t *buffer, int bufferSize){ + if (! CHECK_BUFFER_VALIDITY ) + return; + + uint16_t runningSum = 0; + uint16_t value = 0; + + int idx = 0; + + for (idx; idx < bufferSize-2; idx += 2){ + memcpy(buffer + idx, &value, 2); + runningSum += value; + } + + memcpy(&runningSum, buffer + idx, 2); + +} + +void generateSimulatedSpike(SpikeObject *s, uint64_t timestamp, int noise) +{ + //std::cout<<"generateSimulatedSpike()"<<std::endl; + + uint16_t trace[][32] = + { + { 880, 900, 940, 1040, 1290, 1790, 2475, 2995, 3110, 2890, + 2505, 2090, 1720, 1410, 1155, 945, 775, 635, 520, 420, + 340, 265, 205, 155, 115, 80, 50, 34, 10, 34, 50, 80}, + { 1040, 1090, 1190, 1350, 1600, 1960, 2380, 2790, 3080, 3140, + 2910, 2430, 1810, 1180, 680, 380, 270, 320, 460, 630, + 770, 870, 940, 970, 990, 1000, 1000, 1000, 1000, 1000, 1000, 1000}, + { 1000, 1000, 1000, 1000, 1000, 1040, 1140, 1440, 2040, 2240, + 2400, 2340, 2280, 1880, 1640, 920, 520, 300, 140, 040, + 20, 20, 40, 100, 260, 500, 740, 900, 960, 1000, 1000, 1000} + }; + + + // We don't want to shift the waveform but scale it, and we don't want to scale + // the baseline, just the peak of the waveform + float scale[32] = + { 1.0, 1.0, 1.0, 1.0, 1.1, 1.2, 1.3, 1.5, 1.7, 2.0, 2.1, 2.2, 2.1, 2.0, 1.7, 1.5, + 1.3, 1.2, 1.1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + + uint16_t gain = 2000; + + s->eventType = SPIKE_EVENT_CODE; + s->timestamp = timestamp; + s->source = 0; + s->nChannels = 4; + s->nSamples = 32; + int idx=0; + + int waveType = rand()%2; // Pick one of the three predefined waveshapes to generate + int shift = 1000 + 32768; + + for (int i=0; i<4; i++) + { + s->gain[i] = gain; + s->threshold[i] = 4000; + double scaleExponent = (double)(rand()%26+2) / 10.0f; // Scale the wave between 50% and 150% + + for (int j=0; j<32; j++){ + + int n = 0; + if (noise>0){ + n = rand() % noise - noise/2; + } + + s->data[idx] = (trace[waveType][j] + n) * pow(double(scale[j]),scaleExponent) + shift; + idx = idx+1; + } + } + +} +void generateEmptySpike(SpikeObject *s, int nChannels){ + + s->eventType = SPIKE_EVENT_CODE; + s->timestamp = 0; + s->source = 0; + s->nChannels = 4; + s->nSamples = 32; + + int idx = 0; + for (int i=0; i<4; i++) + { + s->gain[i] = 0; + s->threshold[i] = 0; + for (int j=0; j<32; j++){ + s->data[idx] = 0; + idx = idx+1; + } + } +} + +void printSpike(SpikeObject *s){ + + std::cout<< " SpikeObject:\n"; + std::cout<< "\tTimestamp:" << s->timestamp; + std::cout<< "\tSource:" << s->source; + std::cout<< "\tnChannels:" <<s->nChannels; + std::cout<<"\tnSamples" << s->nSamples; + std::cout<<"\n\t 8 Data Samples:"; + for (int i=0; i<8; i++) + std::cout<<s->data+i<<" "; + std::cout<<std::endl; +} diff --git a/Source/Processors/Visualization/SpikePlotting/PlotUtils.cpp b/Source/Processors/Visualization/SpikePlotting/PlotUtils.cpp index 2f97c36489cc6253f764577880586681ee5ab622..f6f9f3ff96316c94765461dff771b78ad094cc6b 100755 --- a/Source/Processors/Visualization/SpikePlotting/PlotUtils.cpp +++ b/Source/Processors/Visualization/SpikePlotting/PlotUtils.cpp @@ -1,229 +1,229 @@ -#include "PlotUtils.h" - -void checkGlError(){ - GLenum errCode; - const GLubyte *errString; - if ((errCode = glGetError()) != GL_NO_ERROR) { - errString = gluErrorString(errCode); - fprintf (stderr, "OpenGL Error: %s\n", errString); - exit(1); - } - else - std::cout<<"OpenGL Okay!"<<std::endl; -} - -void drawString(float x, float y, void *f, const char *string){ - glRasterPos2f(x, y); - int len = strlen(string); - // glColor3f(1.0, 1.0, 1.0); - for (int i = 0; i < len; i++) { - //glutBitmapCharacter(f, string[i]); - } -} - -void drawString(float x, float y, int size, String s, FTPixmapFont* f){ - - glRasterPos2f(x, y); - - f->FaceSize(size); - f->Render(s); -} - -void drawViewportEdge(){ - - glPushMatrix(); - glLoadIdentity(); - - glBegin(GL_LINE_LOOP); - glVertex2f(-.995, -.995); - glVertex2f( .995, -.995); - glVertex2f( .995, .995); - glVertex2f(-.995, .995); - glEnd(); - - glPopMatrix(); -} - -void drawViewportCross(){ - - glColor3f(0.0,1.0,1.0); - - glPushMatrix(); - glLoadIdentity(); - - glBegin(GL_LINE_LOOP); - glVertex2f(-.995, -.995); - glVertex2f( .995, .995); - glVertex2f( .995, -.995); - glVertex2f(-.995, .995); - glEnd(); - - glPopMatrix(); -} - -void setViewportRange(int xMin,int yMin,int xMax,int yMax){ - - float dx = xMax-xMin; - float dy = yMax-yMin; - -// printf("Setting viewport to:%d,%d %d,%d with dims%d,%d %d,%d\n", x,y,w,h, xMin, xMin, xMax,yMax); -// printf("Dx:%f Dy:%f, Scaling viewport by:%f,%f \n", dx,dy,2.0/dx, 2.0/dy); - glLoadIdentity(); - glTranslatef(-1.0,-1.0,0.0); - glScalef(2.0f/dx, 2.0f/dy, 1.0); - glTranslatef(0-xMin, 0-yMin, 0); - -} -int roundUp(int numToRound, int multiple) -{ - if(multiple == 0) - { - return numToRound; - } - - int remainder = numToRound % multiple; - if (remainder == 0) - return numToRound; - return numToRound + multiple - remainder; -} - -double ad16ToUv(int x, int gain){ - int result = (double)(x * 20e6) / (double)(gain * pow(2,16)); - return result; -} - -void makeLabel(int val, int gain, bool convert, char * s){ - if (convert){ - double volt = ad16ToUv(val, gain)/1000.; - if (abs(val)>1e6){ - //val = val/(1e6); - sprintf(s, "%.2fV", volt); - } - else if(abs(val)>1e3){ - //val = val/(1e3); - sprintf(s, "%.2fmV", volt); - } - else - sprintf(s, "%.2fuV", volt); - } - else - sprintf(s,"%d", (int)val); -} - -void 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; -} - - -bool isFrameBufferExtensionSupported(){ - - std::cout<<"Checking to see if the OpenGL Frame Buffer Extension is Supported"<<std::endl; - - char* str = 0; - char* tok = 0; - - std::string fboExt = "GL_EXT_framebuffer_object"; - - str = (char*)glGetString(GL_EXTENSIONS); - - if(str) - { - std::vector <std::string> extensions; - tok = strtok((char*)str, " "); - while(tok) - { - - std::string ext = tok; - - if (ext == fboExt) - return true; - - tok = strtok(0, " "); - } - return false; - } - else - return false; -} - -bool checkFramebufferStatus() -{ - // check FBO status - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - return status == GL_FRAMEBUFFER_COMPLETE_EXT; - - switch(status) - { - case GL_FRAMEBUFFER_COMPLETE_EXT: - std::cout << "Framebuffer complete." << std::endl; - return true; - - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: - std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl; - return false; - - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: - std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl; - return false; - - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: - std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl; - return false; - - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: - std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl; - return false; - - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: - std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl; - return false; - - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: - std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl; - return false; - - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - std::cout << "[ERROR] Unsupported by FBO implementation." << std::endl; - return false; - - default: - std::cout << "[ERROR] Unknown error." << std::endl; - return false; - } -} - -// std::addressof was introduced in C++11, an equivalent function is defined below -// definition from http://en.cppreference.com/w/cpp/memory/addressof - +#include "PlotUtils.h" + +void checkGlError(){ + GLenum errCode; + const GLubyte *errString; + if ((errCode = glGetError()) != GL_NO_ERROR) { + errString = gluErrorString(errCode); + fprintf (stderr, "OpenGL Error: %s\n", errString); + exit(1); + } + else + std::cout<<"OpenGL Okay!"<<std::endl; +} + +void drawString(float x, float y, void *f, const char *string){ + glRasterPos2f(x, y); + int len = strlen(string); + // glColor3f(1.0, 1.0, 1.0); + for (int i = 0; i < len; i++) { + //glutBitmapCharacter(f, string[i]); + } +} + +void drawString(float x, float y, int size, String s, FTPixmapFont* f){ + + glRasterPos2f(x, y); + + f->FaceSize(size); + f->Render(s); +} + +void drawViewportEdge(){ + + glPushMatrix(); + glLoadIdentity(); + + glBegin(GL_LINE_LOOP); + glVertex2f(-.995, -.995); + glVertex2f( .995, -.995); + glVertex2f( .995, .995); + glVertex2f(-.995, .995); + glEnd(); + + glPopMatrix(); +} + +void drawViewportCross(){ + + glColor3f(0.0,1.0,1.0); + + glPushMatrix(); + glLoadIdentity(); + + glBegin(GL_LINE_LOOP); + glVertex2f(-.995, -.995); + glVertex2f( .995, .995); + glVertex2f( .995, -.995); + glVertex2f(-.995, .995); + glEnd(); + + glPopMatrix(); +} + +void setViewportRange(int xMin,int yMin,int xMax,int yMax){ + + float dx = xMax-xMin; + float dy = yMax-yMin; + +// printf("Setting viewport to:%d,%d %d,%d with dims%d,%d %d,%d\n", x,y,w,h, xMin, xMin, xMax,yMax); +// printf("Dx:%f Dy:%f, Scaling viewport by:%f,%f \n", dx,dy,2.0/dx, 2.0/dy); + glLoadIdentity(); + glTranslatef(-1.0,-1.0,0.0); + glScalef(2.0f/dx, 2.0f/dy, 1.0); + glTranslatef(0-xMin, 0-yMin, 0); + +} +int roundUp(int numToRound, int multiple) +{ + if(multiple == 0) + { + return numToRound; + } + + int remainder = numToRound % multiple; + if (remainder == 0) + return numToRound; + return numToRound + multiple - remainder; +} + +double ad16ToUv(int x, int gain){ + int result = (double)(x * 20e6) / (double)(gain * pow(2.0,16)); + return result; +} + +void makeLabel(int val, int gain, bool convert, char * s){ + if (convert){ + double volt = ad16ToUv(val, gain)/1000.; + if (abs(val)>1e6){ + //val = val/(1e6); + sprintf(s, "%.2fV", volt); + } + else if(abs(val)>1e3){ + //val = val/(1e3); + sprintf(s, "%.2fmV", volt); + } + else + sprintf(s, "%.2fuV", volt); + } + else + sprintf(s,"%d", (int)val); +} + +void 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; +} + + +bool isFrameBufferExtensionSupported(){ + + std::cout<<"Checking to see if the OpenGL Frame Buffer Extension is Supported"<<std::endl; + + char* str = 0; + char* tok = 0; + + std::string fboExt = "GL_EXT_framebuffer_object"; + + str = (char*)glGetString(GL_EXTENSIONS); + + if(str) + { + std::vector <std::string> extensions; + tok = strtok((char*)str, " "); + while(tok) + { + + std::string ext = tok; + + if (ext == fboExt) + return true; + + tok = strtok(0, " "); + } + return false; + } + else + return false; +} + +bool checkFramebufferStatus() +{ + // check FBO status + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + return status == GL_FRAMEBUFFER_COMPLETE_EXT; + + switch(status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + std::cout << "Framebuffer complete." << std::endl; + return true; + + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl; + return false; + + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl; + return false; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + std::cout << "[ERROR] Unsupported by FBO implementation." << std::endl; + return false; + + default: + std::cout << "[ERROR] Unknown error." << std::endl; + return false; + } +} + +// std::addressof was introduced in C++11, an equivalent function is defined below +// definition from http://en.cppreference.com/w/cpp/memory/addressof + diff --git a/Source/Processors/Visualization/SpikePlotting/ProjectionAxes.cpp b/Source/Processors/Visualization/SpikePlotting/ProjectionAxes.cpp index 2a57c424e151adbec3ed3cc7376b421b4dcbd6a2..44cfe03ba0fd26573b9d6adde79e1ece15dce0f4 100755 --- a/Source/Processors/Visualization/SpikePlotting/ProjectionAxes.cpp +++ b/Source/Processors/Visualization/SpikePlotting/ProjectionAxes.cpp @@ -1,350 +1,350 @@ -#include "ProjectionAxes.h" - -ProjectionAxes::ProjectionAxes(): - GenericAxes(), - drawGrid(false), - overlay(false), - convertLabelUnits(true), - buffIdx(-1), - totalSpikes(0), - newSpike(false), - isTextureValid(false), - fboCreated(false), - allSpikesNextRender(false) -{ - GenericAxes::type = PROJ1x2; - GenericAxes::gotFirstSpike = false; - - ylims[0] = 0; - ylims[1] = 1; - setPointColor(1.0,1.0,1.0); - n2ProjIdx(type, &Dim1, &Dim2); - - clearOnNextDraw = false; - -} - -ProjectionAxes::ProjectionAxes(int x, int y, double w, double h, int t): - GenericAxes(x,y,w,h,t), - drawGrid(true), - overlay(false), - convertLabelUnits(true), - buffIdx(-1), - totalSpikes(0), - newSpike(false), - isTextureValid(false), - fboCreated(false), - allSpikesNextRender(false) -{ - GenericAxes::gotFirstSpike = false; - - setPointColor(1.0,1.0,1.0); - - n2ProjIdx(type, &Dim1, &Dim2); - - clearOnNextDraw = false; -} - -void ProjectionAxes::updateSpikeData(SpikeObject s){ - //std::cout<<"ProjectionAxes::updateSpikeData()"<<std::endl; - GenericAxes::updateSpikeData(s); - - buffIdx++; - if (buffIdx >= AMP_BUFF_MAX_SIZE) - buffIdx %= AMP_BUFF_MAX_SIZE; - - int idx1, idx2; - calcWaveformPeakIdx(ampDim1,ampDim2,&idx1, &idx2); - - ampBuffer[0][buffIdx] = (s.data[idx1] - 32768); - ampBuffer[1][buffIdx] = (s.data[idx2] - 32768); - newSpike = true; -} - -void ProjectionAxes::redraw(){ - - BaseUIElement::redraw(); - - plot(); - - BaseUIElement::drawElementEdges(); -} - - -void ProjectionAxes::plot(){ - //setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); - - GLenum errCode; - const GLubyte *errString; - - // Should we plot all spikes to the texture or just the newest one - bool allSpikes = false; - - if (!isTextureValid){ - std::cout<<"ProjectionAxes::plot() --> Texture is invalid regenerating it!"<<std::endl; - createTexture(); - allSpikes = true; - } - - if (clearOnNextDraw){ - clearTexture(); - clearOnNextDraw = false; - } - - drawSpikesToTexture(allSpikes); - drawTexturedQuad(); - plotNewestSpike(); - - // if there has been an openGL error we need to rerender the texture and replot everything - // errors occur when the openGL context has been destroyed and recreated. I'm not sure how to - // explicitly catch that event so instead we check for a drawing error. - if ((errCode = glGetError()) != GL_NO_ERROR) { - errString = gluErrorString(errCode); - std::cout<<"OpenGL Error:"<< errString << "! Invalidating and rerendering the texture!" << std::endl; - - invalidateTexture(); - plot(); - return; - } -// else -// std::cout<<"All is good no errors detected"<<std::endl; -} - -void ProjectionAxes::plotOldSpikes(bool allSpikes){ - //std::cout<<"ProjectionAxes::plotOldSpikes() allSpikes:"<<allSpikes<<std::endl; - - //set the viewport to the size of the texture - glViewport(0,0,texWidth, texHeight); - - //set the plotting range for the viewport to the limits of the plot - setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); - - // if allSpikes plot start with 1 else start with buffIdx - int startIdx = (allSpikes) ? 1 : buffIdx; - // either plot to totalSpikes or the end of the buffer if total spikes has wrapped - int stopIdx = (totalSpikes > AMP_BUFF_MAX_SIZE) ? AMP_BUFF_MAX_SIZE : buffIdx; - - // if (allSpikes) - // std::cout<<"\tUpdating texture with all spikes: "<< stopIdx - startIdx + 1 <<std::endl; - - glColor3f(1.0, 1.0, 1.0); - glPointSize(1); - glBegin(GL_POINTS); - for (int i=startIdx; i<=stopIdx; i++) - glVertex2i(ampBuffer[0][i], ampBuffer[1][i]); - glEnd(); - -} - - -void ProjectionAxes::plotNewestSpike(){ - - BaseUIElement::setGlViewport(); - setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); - - // draw the newest spike as a big red point so it stands out against the old spikes - glColor3f(1.0, 0.0, 0.0); - glPointSize(4); - - if (gotFirstSpike) - { - - glBegin(GL_POINTS); - glVertex2i(ampBuffer[0][buffIdx], ampBuffer[1][buffIdx]); - glEnd(); - } - - newSpike = false; -} - - void ProjectionAxes::calcWaveformPeakIdx(int d1, int d2, int *idx1, int *idx2){ - - int max1 = -1*pow(2,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::setPosition(int x, int y, int w, int h){ - // std::cout<<"ProjectionAxes::setPosition()"<<std::endl; - - // only invalidate the texture if its size has actually changed - if (w!=GenericAxes::width || h!=GenericAxes::height) - invalidateTexture(); - - GenericAxes::setPosition(x,y,w,h); -} - -void ProjectionAxes::setPointColor(GLfloat r, GLfloat g, GLfloat b){ - pointColor[0] = r; - pointColor[1] = g; - pointColor[2] = b; -} -void ProjectionAxes::setGridColor(GLfloat r, GLfloat g, GLfloat b){ - gridColor[0] = r; - gridColor[1] = g; - gridColor[2] = b; -} - - -void ProjectionAxes::createTexture(){ - - texWidth = BaseUIElement::width; - texHeight = BaseUIElement::height; - - std::cout<<"Creating a new texture of size:"<<texWidth<<"x"<<texHeight;//<<std::endl; - // Delete the old texture - glDeleteTextures(1, &textureId); - // Generate a new texture - glGenTextures(1, &textureId); - std::cout<<" textureId:"<<textureId<<std::endl; - // Bind the texture, and set the appropriate parameters - glBindTexture(GL_TEXTURE_2D, textureId); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glGenerateMipmap(GL_TEXTURE_2D); - // generate a new FrameBufferObject - createFBO(); - - // the texture should now be valid, set the flag appropriately - isTextureValid = true; - -} - -void ProjectionAxes::createFBO(){ - std::cout<<"Creating a new FBO, is already created?:"<<fboCreated<<" ";//<<std::endl; - - // if (!isTextureValid) - // createTexture(); - // Delete the old frame buffer, render buffer - if (fboCreated){ - glDeleteFramebuffers(1, &fboId); - glDeleteRenderbuffers(1, &rboId); - } - - // Generate and Bind the frame buffer - glGenFramebuffersEXT(1, &fboId); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); - - // Generate and bind the new Render Buffer - glGenRenderbuffersEXT(1, &rboId); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId); - std::cout << " fboID:" << fboId << " rboID:" << rboId << std::endl; - - - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, texWidth, texHeight); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - - // Attach the texture to the framebuffer - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboId); - - // If the FrameBuffer wasn't created then we have a bigger problem. Abort the program. - if(!checkFramebufferStatus()){ - std::cout<<"FrameBufferObject not created! Are you running the newest version of OpenGL?"<<std::endl; - std::cout<<"FrameBufferObjects are REQUIRED! Quitting!"<<std::endl; - exit(1); - } - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - fboCreated = true; -} - - -void ProjectionAxes::drawSpikesToTexture(bool allSpikes){ - - //std::cout<<"ProjectionAxes::drawSpikesToTexture() plotting all spikes:"<<allSpikes<<std::endl; - - - // For some reason if we want to plot ALL the spikes to a texture we must plot two draw cycles - // in a row, perhaps this has to do with double buffering, I'm not sure why... investigate this! - - // if the allSpikes flag is set we set the allSpikesNextRender as true so we plot next - // all spikes next render cycle too, if only the allSpikesNextRender is true we set all spikes - // to true and allSpikes next render to false. - - //Basically this logic ensures that if allSpikes is ever set to true it will be set to true - //on the next call to drawSpikesToTexture() regardless of what value it is actually set to - if (allSpikes) - allSpikesNextRender = true; - else if (!allSpikes && allSpikesNextRender) - { - allSpikes = true; - allSpikesNextRender = false; - } - // set the rendering destination to FBO - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); - - // plot to the texture - if (gotFirstSpike) - plotOldSpikes(allSpikes); - - // bind the original FrameBuffer - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); -} - -void ProjectionAxes::invalidateTexture(){ - isTextureValid = false; -} - - -void ProjectionAxes::drawTexturedQuad(){ - BaseUIElement::setGlViewport(); - // We need to scale the viewport in this case because we want to fill it with a quad. - // if we load identity then we can use a quad bound by (-1, 1); - glLoadIdentity(); - - // Bind the texture to render - glBindTexture(GL_TEXTURE_2D, textureId); - - // Build the quad - int size = 1; - int texS = 1; - glBegin(GL_QUADS); - glColor4f(1, 1, 1, 1); - glTexCoord2f(texS, texS); glVertex3f(size, size,0); - glTexCoord2f(0, texS); glVertex3f(-1 * size , size,0); - glTexCoord2f(0, 0); glVertex3f(-1 * size, -1 * size,0); - glTexCoord2f(texS, 0); glVertex3f(size, -1 * size,0); - glEnd(); - - // Unbind the texture - glBindTexture(GL_TEXTURE_2D, 0); -} - -void ProjectionAxes::clear(){ - - //reset buffIDx and totalSpikes - buffIdx = 0; - totalSpikes = 0; - - // set flag to clear on next draw - clearOnNextDraw = true; -} - -void ProjectionAxes::clearTexture(){ - std::cout<<"ProjectinAxes::clearTexture() --> Clearing the Texture!"<<std::endl; - - glViewport(0,0,texWidth, texHeight); - setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); - - // set the rendering destination to FBO - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); - // Clear the framebufferObject - glClearColor (0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +#include "ProjectionAxes.h" + +ProjectionAxes::ProjectionAxes(): + GenericAxes(), + drawGrid(false), + overlay(false), + convertLabelUnits(true), + buffIdx(-1), + totalSpikes(0), + newSpike(false), + isTextureValid(false), + fboCreated(false), + allSpikesNextRender(false) +{ + GenericAxes::type = PROJ1x2; + GenericAxes::gotFirstSpike = false; + + ylims[0] = 0; + ylims[1] = 1; + setPointColor(1.0,1.0,1.0); + n2ProjIdx(type, &Dim1, &Dim2); + + clearOnNextDraw = false; + +} + +ProjectionAxes::ProjectionAxes(int x, int y, double w, double h, int t): + GenericAxes(x,y,w,h,t), + drawGrid(true), + overlay(false), + convertLabelUnits(true), + buffIdx(-1), + totalSpikes(0), + newSpike(false), + isTextureValid(false), + fboCreated(false), + allSpikesNextRender(false) +{ + GenericAxes::gotFirstSpike = false; + + setPointColor(1.0,1.0,1.0); + + n2ProjIdx(type, &Dim1, &Dim2); + + clearOnNextDraw = false; +} + +void ProjectionAxes::updateSpikeData(SpikeObject s){ + //std::cout<<"ProjectionAxes::updateSpikeData()"<<std::endl; + GenericAxes::updateSpikeData(s); + + buffIdx++; + if (buffIdx >= AMP_BUFF_MAX_SIZE) + buffIdx %= AMP_BUFF_MAX_SIZE; + + int idx1, idx2; + calcWaveformPeakIdx(ampDim1,ampDim2,&idx1, &idx2); + + ampBuffer[0][buffIdx] = (s.data[idx1] - 32768); + ampBuffer[1][buffIdx] = (s.data[idx2] - 32768); + newSpike = true; +} + +void ProjectionAxes::redraw(){ + + BaseUIElement::redraw(); + + plot(); + + BaseUIElement::drawElementEdges(); +} + + +void ProjectionAxes::plot(){ + //setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); + + GLenum errCode; + const GLubyte *errString; + + // Should we plot all spikes to the texture or just the newest one + bool allSpikes = false; + + if (!isTextureValid){ + std::cout<<"ProjectionAxes::plot() --> Texture is invalid regenerating it!"<<std::endl; + createTexture(); + allSpikes = true; + } + + if (clearOnNextDraw){ + clearTexture(); + clearOnNextDraw = false; + } + + drawSpikesToTexture(allSpikes); + drawTexturedQuad(); + plotNewestSpike(); + + // if there has been an openGL error we need to rerender the texture and replot everything + // errors occur when the openGL context has been destroyed and recreated. I'm not sure how to + // explicitly catch that event so instead we check for a drawing error. + if ((errCode = glGetError()) != GL_NO_ERROR) { + errString = gluErrorString(errCode); + std::cout<<"OpenGL Error:"<< errString << "! Invalidating and rerendering the texture!" << std::endl; + + invalidateTexture(); + plot(); + return; + } +// else +// std::cout<<"All is good no errors detected"<<std::endl; +} + +void ProjectionAxes::plotOldSpikes(bool allSpikes){ + //std::cout<<"ProjectionAxes::plotOldSpikes() allSpikes:"<<allSpikes<<std::endl; + + //set the viewport to the size of the texture + glViewport(0,0,texWidth, texHeight); + + //set the plotting range for the viewport to the limits of the plot + setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); + + // if allSpikes plot start with 1 else start with buffIdx + int startIdx = (allSpikes) ? 1 : buffIdx; + // either plot to totalSpikes or the end of the buffer if total spikes has wrapped + int stopIdx = (totalSpikes > AMP_BUFF_MAX_SIZE) ? AMP_BUFF_MAX_SIZE : buffIdx; + + // if (allSpikes) + // std::cout<<"\tUpdating texture with all spikes: "<< stopIdx - startIdx + 1 <<std::endl; + + glColor3f(1.0, 1.0, 1.0); + glPointSize(1); + glBegin(GL_POINTS); + for (int i=startIdx; i<=stopIdx; i++) + glVertex2i(ampBuffer[0][i], ampBuffer[1][i]); + glEnd(); + +} + + +void ProjectionAxes::plotNewestSpike(){ + + BaseUIElement::setGlViewport(); + setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); + + // draw the newest spike as a big red point so it stands out against the old spikes + glColor3f(1.0, 0.0, 0.0); + glPointSize(4); + + if (gotFirstSpike) + { + + glBegin(GL_POINTS); + glVertex2i(ampBuffer[0][buffIdx], ampBuffer[1][buffIdx]); + glEnd(); + } + + newSpike = false; +} + + void ProjectionAxes::calcWaveformPeakIdx(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::setPosition(int x, int y, int w, int h){ + // std::cout<<"ProjectionAxes::setPosition()"<<std::endl; + + // only invalidate the texture if its size has actually changed + if (w!=GenericAxes::width || h!=GenericAxes::height) + invalidateTexture(); + + GenericAxes::setPosition(x,y,w,h); +} + +void ProjectionAxes::setPointColor(GLfloat r, GLfloat g, GLfloat b){ + pointColor[0] = r; + pointColor[1] = g; + pointColor[2] = b; +} +void ProjectionAxes::setGridColor(GLfloat r, GLfloat g, GLfloat b){ + gridColor[0] = r; + gridColor[1] = g; + gridColor[2] = b; +} + + +void ProjectionAxes::createTexture(){ + + texWidth = BaseUIElement::width; + texHeight = BaseUIElement::height; + + std::cout<<"Creating a new texture of size:"<<texWidth<<"x"<<texHeight;//<<std::endl; + // Delete the old texture + glDeleteTextures(1, &textureId); + // Generate a new texture + glGenTextures(1, &textureId); + std::cout<<" textureId:"<<textureId<<std::endl; + // Bind the texture, and set the appropriate parameters + glBindTexture(GL_TEXTURE_2D, textureId); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glGenerateMipmap(GL_TEXTURE_2D); + // generate a new FrameBufferObject + createFBO(); + + // the texture should now be valid, set the flag appropriately + isTextureValid = true; + +} + +void ProjectionAxes::createFBO(){ + std::cout<<"Creating a new FBO, is already created?:"<<fboCreated<<" ";//<<std::endl; + + // if (!isTextureValid) + // createTexture(); + // Delete the old frame buffer, render buffer + if (fboCreated){ + glDeleteFramebuffers(1, &fboId); + glDeleteRenderbuffers(1, &rboId); + } + + // Generate and Bind the frame buffer + glGenFramebuffersEXT(1, &fboId); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); + + // Generate and bind the new Render Buffer + glGenRenderbuffersEXT(1, &rboId); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId); + std::cout << " fboID:" << fboId << " rboID:" << rboId << std::endl; + + + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, texWidth, texHeight); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + // Attach the texture to the framebuffer + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboId); + + // If the FrameBuffer wasn't created then we have a bigger problem. Abort the program. + if(!checkFramebufferStatus()){ + std::cout<<"FrameBufferObject not created! Are you running the newest version of OpenGL?"<<std::endl; + std::cout<<"FrameBufferObjects are REQUIRED! Quitting!"<<std::endl; + exit(1); + } + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + fboCreated = true; +} + + +void ProjectionAxes::drawSpikesToTexture(bool allSpikes){ + + //std::cout<<"ProjectionAxes::drawSpikesToTexture() plotting all spikes:"<<allSpikes<<std::endl; + + + // For some reason if we want to plot ALL the spikes to a texture we must plot two draw cycles + // in a row, perhaps this has to do with double buffering, I'm not sure why... investigate this! + + // if the allSpikes flag is set we set the allSpikesNextRender as true so we plot next + // all spikes next render cycle too, if only the allSpikesNextRender is true we set all spikes + // to true and allSpikes next render to false. + + //Basically this logic ensures that if allSpikes is ever set to true it will be set to true + //on the next call to drawSpikesToTexture() regardless of what value it is actually set to + if (allSpikes) + allSpikesNextRender = true; + else if (!allSpikes && allSpikesNextRender) + { + allSpikes = true; + allSpikesNextRender = false; + } + // set the rendering destination to FBO + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); + + // plot to the texture + if (gotFirstSpike) + plotOldSpikes(allSpikes); + + // bind the original FrameBuffer + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +} + +void ProjectionAxes::invalidateTexture(){ + isTextureValid = false; +} + + +void ProjectionAxes::drawTexturedQuad(){ + BaseUIElement::setGlViewport(); + // We need to scale the viewport in this case because we want to fill it with a quad. + // if we load identity then we can use a quad bound by (-1, 1); + glLoadIdentity(); + + // Bind the texture to render + glBindTexture(GL_TEXTURE_2D, textureId); + + // Build the quad + int size = 1; + int texS = 1; + glBegin(GL_QUADS); + glColor4f(1, 1, 1, 1); + glTexCoord2f(texS, texS); glVertex3f(size, size,0); + glTexCoord2f(0, texS); glVertex3f(-1 * size , size,0); + glTexCoord2f(0, 0); glVertex3f(-1 * size, -1 * size,0); + glTexCoord2f(texS, 0); glVertex3f(size, -1 * size,0); + glEnd(); + + // Unbind the texture + glBindTexture(GL_TEXTURE_2D, 0); +} + +void ProjectionAxes::clear(){ + + //reset buffIDx and totalSpikes + buffIdx = 0; + totalSpikes = 0; + + // set flag to clear on next draw + clearOnNextDraw = true; +} + +void ProjectionAxes::clearTexture(){ + std::cout<<"ProjectinAxes::clearTexture() --> Clearing the Texture!"<<std::endl; + + glViewport(0,0,texWidth, texHeight); + setViewportRange(xlims[0], ylims[0], xlims[1], ylims[1]); + + // set the rendering destination to FBO + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); + // Clear the framebufferObject + glClearColor (0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } \ No newline at end of file diff --git a/Source/UI/ControlPanel.cpp b/Source/UI/ControlPanel.cpp index 9fc589f26ba6ab754dfbc7d2c8ddd90aff325c82..7a1a6a3e4845348b02136c5682331232858a0258 100755 --- a/Source/UI/ControlPanel.cpp +++ b/Source/UI/ControlPanel.cpp @@ -208,16 +208,16 @@ void Clock::drawTime() if (isRecording) { glColor4f(1.0, 0.0, 0.0, 1.0); - m = floor(totalRecordTime/60000); - s = floor((totalRecordTime - m*60000)/1000); + m = floor(totalRecordTime/60000.0); + s = floor((totalRecordTime - m*60000.0)/1000.0); } else { if (isRunning) glColor4f(1.0, 1.0, 0.0, 1.0); else glColor4f(1.0, 1.0, 1.0, 1.0); - m = floor(totalTime/60000); - s = floor((totalTime - m*60000)/1000); + m = floor(totalTime/60000.0); + s = floor((totalTime - m*60000.0)/1000.0); } String timeString = "";