#include "TimeSeriesCanvas.h" #include "../CommonInterfaces/Common2dCanvasInterface.h" #include "LinearMath/btAlignedObjectArray.h" #include "TimeSeriesFontData.h" #include "LinearMath/btVector3.h" #include struct DataSource { unsigned char m_red; unsigned char m_green; unsigned char m_blue; float m_lastValue; bool m_hasLastValue; DataSource() : m_hasLastValue(false) { } }; struct TimeSeriesInternalData { btAlignedObjectArray m_dataSources; struct Common2dCanvasInterface* m_canvasInterface; int m_canvasIndex; int m_width; int m_height; float m_pixelsPerUnit; float m_zero; int m_timeTicks; int m_ticksPerSecond; float m_yScale; int m_bar; unsigned char m_backgroundRed; unsigned char m_backgroundGreen; unsigned char m_backgroundBlue; unsigned char m_backgroundAlpha; unsigned char m_textColorRed; unsigned char m_textColorGreen; unsigned char m_textColorBlue; unsigned char m_textColorAlpha; float getTime() { return m_timeTicks / (float)m_ticksPerSecond; } TimeSeriesInternalData(int width, int height) : m_width(width), m_height(height), m_pixelsPerUnit(-100), m_zero(height / 2.0), m_timeTicks(0), m_ticksPerSecond(100), m_yScale(1), m_bar(0), m_backgroundRed(255), m_backgroundGreen(255), m_backgroundBlue(255), m_backgroundAlpha(255), m_textColorRed(0), m_textColorGreen(0), m_textColorBlue(255), m_textColorAlpha(255) { } }; TimeSeriesCanvas::TimeSeriesCanvas(struct Common2dCanvasInterface* canvasInterface, int width, int height, const char* windowTitle) { m_internalData = new TimeSeriesInternalData(width, height); m_internalData->m_canvasInterface = canvasInterface; if (canvasInterface) { m_internalData->m_canvasIndex = m_internalData->m_canvasInterface->createCanvas(windowTitle, m_internalData->m_width, m_internalData->m_height, 20, 50); } } void TimeSeriesCanvas::addDataSource(const char* dataSourceLabel, unsigned char red, unsigned char green, unsigned char blue) { DataSource dataSource; dataSource.m_red = red; dataSource.m_green = green; dataSource.m_blue = blue; dataSource.m_lastValue = 0; dataSource.m_hasLastValue = false; if (dataSourceLabel) { int numSources = m_internalData->m_dataSources.size(); int row = numSources % 3; int column = numSources / 3; grapicalPrintf(dataSourceLabel, sTimeSeriesFontData, 50 + 200 * column, m_internalData->m_height - 48 + row * 16, red, green, blue, 255); } m_internalData->m_dataSources.push_back(dataSource); } void TimeSeriesCanvas::setupTimeSeries(float yScale, int ticksPerSecond, int startTime, bool clearCanvas) { if (0 == m_internalData->m_canvasInterface) return; m_internalData->m_pixelsPerUnit = -(m_internalData->m_height / 3.f) / yScale; m_internalData->m_ticksPerSecond = ticksPerSecond; m_internalData->m_yScale = yScale; m_internalData->m_dataSources.clear(); if (clearCanvas) { for (int i = 0; i < m_internalData->m_width; i++) { for (int j = 0; j < m_internalData->m_height; j++) { m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, i, j, m_internalData->m_backgroundRed, m_internalData->m_backgroundGreen, m_internalData->m_backgroundBlue, m_internalData->m_backgroundAlpha); } } } float zeroPixelCoord = m_internalData->m_zero; float pixelsPerUnit = m_internalData->m_pixelsPerUnit; float yPos = zeroPixelCoord + pixelsPerUnit * yScale; float yNeg = zeroPixelCoord + pixelsPerUnit * -yScale; grapicalPrintf("0", sTimeSeriesFontData, 2, zeroPixelCoord, m_internalData->m_textColorRed, m_internalData->m_textColorGreen, m_internalData->m_textColorBlue, m_internalData->m_textColorAlpha); char label[1024]; sprintf(label, "%2.1f", yScale); grapicalPrintf(label, sTimeSeriesFontData, 2, yPos, m_internalData->m_textColorRed, m_internalData->m_textColorGreen, m_internalData->m_textColorBlue, m_internalData->m_textColorAlpha); sprintf(label, "%2.1f", -yScale); grapicalPrintf(label, sTimeSeriesFontData, 2, yNeg, m_internalData->m_textColorRed, m_internalData->m_textColorGreen, m_internalData->m_textColorBlue, m_internalData->m_textColorAlpha); m_internalData->m_canvasInterface->refreshImageData(m_internalData->m_canvasIndex); } TimeSeriesCanvas::~TimeSeriesCanvas() { if (m_internalData->m_canvasInterface && m_internalData->m_canvasIndex >= 0) { m_internalData->m_canvasInterface->destroyCanvas(m_internalData->m_canvasIndex); } delete m_internalData; } float TimeSeriesCanvas::getCurrentTime() const { return m_internalData->getTime(); } void TimeSeriesCanvas::grapicalPrintf(const char* str, void* fontData, int rasterposx, int rasterposy, unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha) { unsigned char c; int x = 0; int xx = 0; while ((c = (unsigned char)*str++)) { x = xx; unsigned char* fontPtr = (unsigned char*)fontData; char ch = c - 32; int sx = ch % 16; int sy = ch / 16; for (int i = sx * 16; i < (sx * 16 + 16); i++) { int y = 0; for (int j = sy * 16; j < (sy * 16 + 16); j++) { unsigned char packedColor = (fontPtr[i * 3 + 255 * 256 * 3 - (256 * j) * 3]); //float colorf = packedColor ? 0.f : 1.f; float colorf = packedColor / 255.f; // ? 0.f : 1.f; btVector4 rgba(colorf, colorf, colorf, 1.f); if (colorf) { if ((rasterposx + x >= 0) && (rasterposx + x < m_internalData->m_width) && (rasterposy + y >= 0) && (rasterposy + y < m_internalData->m_height)) { m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, rasterposx + x, rasterposy + y, red, green, blue, alpha); } } y++; } x++; } xx += 10; } } void TimeSeriesCanvas::shift1PixelToLeft() { int resetVal = 10; int countdown = resetVal; //shift pixture one pixel to the left for (int j = 50; j < m_internalData->m_height - 48; j++) { for (int i = 40; i < this->m_internalData->m_width; i++) { unsigned char red, green, blue, alpha; m_internalData->m_canvasInterface->getPixel(m_internalData->m_canvasIndex, i, j, red, green, blue, alpha); m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, i - 1, j, red, green, blue, alpha); } if (!m_internalData->m_bar) { if (!countdown--) { countdown = resetVal; m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, j, 0, 0, 0, 255); } else { m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, j, 255, 255, 255, 255); } } else { m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, j, 255, 255, 255, 255); } } { int resetVal = 2; static int countdown = resetVal; if (!countdown--) { countdown = resetVal; m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, m_internalData->m_zero, 0, 0, 0, 255); } } { int resetVal = 10; static int countdown = resetVal; if (!countdown--) { countdown = resetVal; float zeroPixelCoord = m_internalData->m_zero; float pixelsPerUnit = m_internalData->m_pixelsPerUnit; float yPos = zeroPixelCoord + pixelsPerUnit * m_internalData->m_yScale; float yNeg = zeroPixelCoord + pixelsPerUnit * -m_internalData->m_yScale; m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, yPos, 0, 0, 0, 255); m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, yNeg, 0, 0, 0, 255); } } if (!m_internalData->m_bar) { char buf[1024]; float time = m_internalData->getTime(); sprintf(buf, "%2.0f", time); grapicalPrintf(buf, sTimeSeriesFontData, m_internalData->m_width - 25, m_internalData->m_zero + 3, 0, 0, 0, 255); m_internalData->m_bar = m_internalData->m_ticksPerSecond; } m_internalData->m_timeTicks++; m_internalData->m_bar--; } void TimeSeriesCanvas::insertDataAtCurrentTime(float orgV, int dataSourceIndex, bool connectToPrevious) { if (0 == m_internalData->m_canvasInterface) return; btAssert(dataSourceIndex < m_internalData->m_dataSources.size()); float zero = m_internalData->m_zero; float amp = m_internalData->m_pixelsPerUnit; //insert some new value(s) in the right-most column { // float time = m_internalData->getTime(); float v = zero + amp * orgV; m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, v, m_internalData->m_dataSources[dataSourceIndex].m_red, m_internalData->m_dataSources[dataSourceIndex].m_green, m_internalData->m_dataSources[dataSourceIndex].m_blue, 255); if (connectToPrevious && m_internalData->m_dataSources[dataSourceIndex].m_hasLastValue) { for (int value = m_internalData->m_dataSources[dataSourceIndex].m_lastValue; value <= v; value++) { if (value >= 0 && value < float(m_internalData->m_height - 1)) { m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, value, m_internalData->m_dataSources[dataSourceIndex].m_red, m_internalData->m_dataSources[dataSourceIndex].m_green, m_internalData->m_dataSources[dataSourceIndex].m_blue, 255); } } for (int value = v; value <= m_internalData->m_dataSources[dataSourceIndex].m_lastValue; value++) { if (value >= 0 && value < float(m_internalData->m_height - 1)) { m_internalData->m_canvasInterface->setPixel(m_internalData->m_canvasIndex, m_internalData->m_width - 1, value, m_internalData->m_dataSources[dataSourceIndex].m_red, m_internalData->m_dataSources[dataSourceIndex].m_green, m_internalData->m_dataSources[dataSourceIndex].m_blue, 255); } } } m_internalData->m_dataSources[dataSourceIndex].m_lastValue = v; m_internalData->m_dataSources[dataSourceIndex].m_hasLastValue = true; } } void TimeSeriesCanvas::nextTick() { shift1PixelToLeft(); m_internalData->m_canvasInterface->refreshImageData(m_internalData->m_canvasIndex); }