From a0bfd689d323334357dc54d4bda27123800ce831 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20Zbyszy=C5=84ski?= <github@mikezed.com>
Date: Wed, 2 May 2018 11:45:10 +0100
Subject: [PATCH] this is in /tools now

---
 .../rapidVisualizer/BarChart.hpp              |  72 +++++
 .../rapidVisualizer/Graph.hpp                 | 122 +++++++++
 .../rapidVisualizer/HistoryGraph.hpp          | 125 +++++++++
 .../rapidVisualizer/LineGraph.hpp             |  85 ++++++
 .../rapidVisualizer/LineGraphHistory.hpp      |  87 ++++++
 .../rapidVisualizer/RapidVisualization.cpp    | 173 ++++++++++++
 .../rapidVisualizer/RapidVisualization.hpp    |  57 ++++
 .../rapidVisualizer/RapidVisualizer.cpp       | 259 ++++++++++++++++++
 .../rapidVisualizer/RapidVisualizer.hpp       |  79 ++++++
 .../rapidVisualizer/RealTimeGraph.hpp         | 107 ++++++++
 .../rapidVisualizer/ScopeWithHistory.hpp      | 184 +++++++++++++
 11 files changed, 1350 insertions(+)
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/BarChart.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/Graph.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/HistoryGraph.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraph.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraphHistory.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.cpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.cpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/RealTimeGraph.hpp
 create mode 100644 examples/ofx/Bitalino_rapidmix/rapidVisualizer/ScopeWithHistory.hpp

diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/BarChart.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/BarChart.hpp
new file mode 100644
index 0000000..379b2df
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/BarChart.hpp
@@ -0,0 +1,72 @@
+//
+//  BarChart.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 09/11/2017.
+//
+//
+
+#ifndef BarChart_hpp
+#define BarChart_hpp
+
+#include <stdio.h>
+#include "RealTimeGraph.hpp"
+
+class BarChart : public RealTimeGraph
+{
+public:
+    BarChart ( GraphState* state ) : RealTimeGraph (state)
+    {
+        //
+    }
+    ~BarChart ( void )
+    {
+        //
+    }
+    void updateRep ( void )
+    {
+        //
+    }
+    void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelRect )
+    {
+        double
+        minIn = 0,
+        minOut = 0,
+        maxOut = -subLabelRect.height,
+        startPosY = subLabelRect.height,
+        barSize = subLabelRect.width/data.labelData.size(),
+        separation = barSize/16,
+        halfSeparation = separation/2;
+        bool drawZeroSep = false;
+        
+        if (data.minValue < 0)
+        { // Add negative part
+            startPosY = subLabelRect.height/2;
+            minIn = -data.maxValue;
+            minOut = subLabelRect.height/2;
+            maxOut /= 2;
+            drawZeroSep = true;
+        }
+        for (uint32_t i = 0; i < data.labelData.size(); ++i)
+        {
+            double output = mapVals(data.labelData[i], minIn, data.maxValue, minOut, maxOut );
+            ofSetColor (graphColor);
+            ofFill();
+            ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
+            
+        }
+        if (drawZeroSep)
+        {
+            ofSetLineWidth(1.25);
+            ofSetColor (175,150,150);
+            ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
+                       subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
+        }
+    }
+    void update ( void )
+    {
+        //
+    }
+};
+
+#endif /* BarChart_hpp */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/Graph.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/Graph.hpp
new file mode 100644
index 0000000..546785f
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/Graph.hpp
@@ -0,0 +1,122 @@
+//
+//  Graph.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 09/11/2017.
+//
+//
+
+#ifndef Graph_h
+#define Graph_h
+
+#include <string>
+#include <vector>
+#include <list>
+#include "ofMain.h"
+#include "ofxGui.h"
+
+// TODO: add namespace, move funcs and struct to other header(s)
+enum class TextAlignment {
+    LEFT,
+    CENTER,
+    RIGHT
+};
+
+static inline void drawTextLabel ( std::string label, ofVec2f position, ofColor labelBackgroundColor, ofColor stringColor, TextAlignment alignment, bool drawAbove )
+{
+    uint32_t strLenPix = label.length()*8;
+    
+    switch (alignment) {
+        case TextAlignment::LEFT:
+            // No exception
+            break;
+        case TextAlignment::CENTER:
+            position.x -= strLenPix / 2;
+            break;
+        case TextAlignment::RIGHT:
+            position.x -= strLenPix;
+            break;
+        default:
+            break;
+    }
+    
+    ofSetColor (labelBackgroundColor);
+    ofRectangle bmpStringRect(position.x - 2,
+                              position.y + ((drawAbove) ? -4 : 12) + 2,
+                              strLenPix + 4, -12);
+    ofDrawRectangle(bmpStringRect);
+    
+    ofSetColor (stringColor);
+    ofDrawBitmapString(label, position.x, position.y + ((drawAbove) ? -4 : 12));
+}
+
+static inline double mapVals ( double x, double in_min, double in_max, double out_min, double out_max )
+{
+    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+}
+
+template <typename T>
+struct DataContainer {
+    T labelData;
+    double minValue = 0.0;
+    double maxValue = 1.0;
+    uint32_t iteratorPos = 0;
+    //ofColor graphColor;
+    
+    void updateMinMax ( void )
+    {
+        auto mm = std::minmax_element(labelData.begin(), labelData.end());
+        double min = labelData[std::distance(labelData.begin(), mm.first)];
+        double max = labelData[std::distance(labelData.begin(), mm.second)];
+        
+        if (min < minValue)
+            minValue = min;
+        if (max > maxValue)
+            maxValue = max;
+        if (fabs(min) > maxValue)
+            maxValue = fabs(min);
+    
+    }
+};
+
+class Graph {
+public:
+    enum graphLayout {
+        GRAPHS_STACKED,
+        GRAPHS_VERTICAL
+    };
+    struct GraphState {
+        std::string label;
+        graphLayout layout = graphLayout::GRAPHS_STACKED;
+        bool hasHistory = false;
+        ofRectangle positionAndSize;
+        uint32_t historySize;
+    };
+    
+    Graph ( GraphState* state )
+    : state(state)
+    {
+        graphColor = ofColor(24, 219, 92);
+        textColor = ofColor(255, 157, 117);
+    }
+    
+    virtual ~Graph ( void )
+    {
+        
+    }
+    
+    virtual void updateRep ( void ) = 0; // update representation
+    virtual void addData ( std::string subLabel, std::vector<double>& data ) = 0;
+    virtual void reset ( void ) = 0;
+    
+    virtual void update ( void ) = 0;
+    virtual void draw ( void ) = 0;
+
+    virtual uint32_t getNumSubGraphs ( void ) const = 0;
+protected:
+    GraphState *state = nullptr;
+    ofColor graphColor;
+    ofColor textColor;
+};
+
+#endif /* Graph_h */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/HistoryGraph.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/HistoryGraph.hpp
new file mode 100644
index 0000000..8086167
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/HistoryGraph.hpp
@@ -0,0 +1,125 @@
+//
+//  HistoryGraph.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 09/11/2017.
+//
+//
+
+#ifndef HistoryGraph_h
+#define HistoryGraph_h
+
+#include "Graph.hpp"
+#include <list>
+
+class HistoryGraph : public Graph
+{
+public:
+    HistoryGraph ( GraphState* state )
+    : Graph(state)
+    {
+        
+    }
+    ~HistoryGraph ( void )
+    {
+    }
+    
+    virtual void updateRep ( void ) = 0; // update representation
+    virtual void drawSubGraph ( std::string subLabel, DataContainer<std::list<double>>& data, ofRectangle subLabelrect ) = 0;
+    virtual void update ( void ) = 0;
+    
+    void addData ( std::string subLabel, std::vector<double>& data )
+    {
+        if (data.size() < state->historySize)
+        {
+            //FIXME: can be sped up by using the result of this search instead of accessing by[] again
+            if ( subLabelData.find(subLabel) == subLabelData.end() ) {
+                // not found
+                DataContainer<std::list<double>> container;
+                container.labelData.resize(state->historySize);
+                std::fill(container.labelData.begin(), container.labelData.end(), 0.0);
+                subLabelData[subLabel] = container;
+                
+            }
+            DataContainer<std::list<double>>&  dataRef = subLabelData[subLabel];
+            list<double>& referencedList = dataRef.labelData;
+            while (referencedList.size() + data.size() >= state->historySize)
+            {
+                referencedList.pop_front();
+            }
+            for (int32_t i = data.size()-1; i >= 0; --i)
+            {
+                double val = data[i];
+                if (val < dataRef.minValue)
+                    dataRef.minValue = val;
+                if (val > dataRef.maxValue)
+                    dataRef.maxValue = val;
+                if (fabs(val) > dataRef.maxValue)
+                    dataRef.maxValue = fabs(val);
+                referencedList.push_back(val);
+            }
+        }
+    }
+    
+    void reset ( void )
+    {
+        subLabelData.clear();
+    }
+    
+    void draw ( void )
+    {
+        uint32_t numElements = subLabelData.size();
+        uint16_t heightBetweenSubLabels = state->positionAndSize.height/numElements;
+        uint16_t subLabelY = 0;
+        
+        ofSetColor (textColor);
+        ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + heightBetweenSubLabels - 1.5,
+                   state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + heightBetweenSubLabels - 1.5);
+         
+        std::map<std::string, DataContainer<std::list<double>>>::iterator it;
+        for(it = subLabelData.begin(); it != subLabelData.end(); ++it)
+        {
+            std::string subLabel = it->first;
+            DataContainer<std::list<double>>& data = it->second;
+            
+            drawSubGraph (subLabel, data, ofRectangle(state->positionAndSize.x,
+                                                      state->positionAndSize.y + subLabelY + 3,
+                                                      state->positionAndSize.width,
+                                                      heightBetweenSubLabels - 6));
+            // Draw label and background
+            drawTextLabel(subLabel, ofVec2f(state->positionAndSize.x + state->positionAndSize.width,
+                                            state->positionAndSize.y + subLabelY),
+                          ofColor(100, 100, 100), ofColor(textColor), TextAlignment::RIGHT, false);
+            
+            
+            // Draw max value
+            drawTextLabel(ofToString(data.maxValue),
+                          ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
+                                  state->positionAndSize.y + subLabelY),
+                          ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, false);
+            
+            // Increment Y position
+            subLabelY += heightBetweenSubLabels;
+            
+            // Draw min value
+            // Show actual min value?
+            drawTextLabel(ofToString((data.minValue < 0) ? -data.maxValue : 0),
+                          ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
+                                  state->positionAndSize.y + subLabelY),
+                          ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, true);
+            
+            // Draw Line at bottom of graph
+            //ofSetLineWidth(2.0);
+            //ofSetColor (180,180,180);
+            /*ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + subLabelY + 3,
+                       state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + subLabelY + 3);*/
+        }
+    }
+    uint32_t getNumSubGraphs ( void ) const
+    {
+        return subLabelData.size();
+    }
+protected:
+    std::map <std::string, DataContainer<std::list<double>>> subLabelData;
+};
+#endif /* HistoryGraph_h */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraph.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraph.hpp
new file mode 100644
index 0000000..88669d9
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraph.hpp
@@ -0,0 +1,85 @@
+//
+//  LineGraph.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 09/11/2017.
+//
+//
+
+#ifndef LineGraph_h
+#define LineGraph_h
+
+#include <stdio.h>
+#include "RealTimeGraph.hpp"
+
+class LineGraph : public RealTimeGraph
+{
+public:
+    LineGraph ( GraphState* state ) : RealTimeGraph (state)
+    {
+        //
+    }
+    ~LineGraph ( void )
+    {
+        //
+    }
+    void updateRep ( void )
+    {
+        //
+    }
+    void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelRect )
+    {
+        double
+        minIn = 0,
+        minOut = 0,
+        maxOut = -subLabelRect.height,
+        startPosY = subLabelRect.height,
+        pointDistance = subLabelRect.width/data.labelData.size(),
+        separation = pointDistance/2;
+        //halfSeparation = separation/2;
+        bool drawZeroSep = false;
+        
+        if (data.minValue < 0)
+        { // Add negative part
+            startPosY = subLabelRect.height/2;
+            minIn = -data.maxValue;
+            minOut = subLabelRect.height/2;
+            maxOut /= 2;
+            drawZeroSep = true;
+        }
+        
+        ofBeginShape();
+        ofFill();
+        ofVertex(subLabelRect.x, subLabelRect.y + startPosY);
+        double output = mapVals(data.labelData[0], minIn, data.maxValue, minOut, maxOut );
+        ofVertex(subLabelRect.x, subLabelRect.y + startPosY + output);
+        for (uint32_t i = 0; i < data.labelData.size(); ++i)
+        {
+            output = mapVals(data.labelData[i], minIn, data.maxValue, minOut, maxOut );
+            ofSetColor (graphColor);
+            
+            ofVertex(subLabelRect.x + pointDistance * i + separation, subLabelRect.y + startPosY + output);
+            //ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
+            
+        }
+        output = mapVals(data.labelData[data.labelData.size()-1], minIn, data.maxValue, minOut, maxOut );
+        ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY + output);
+        ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
+        ofEndShape();
+        
+        if (drawZeroSep)
+        {
+            ofSetLineWidth(1.25);
+            ofSetColor (175,150,150);
+            ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
+                       subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
+        }
+    }
+    void update ( void )
+    {
+        //
+    }
+};
+
+
+#endif /* LineGraph_h */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraphHistory.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraphHistory.hpp
new file mode 100644
index 0000000..e3b32f8
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/LineGraphHistory.hpp
@@ -0,0 +1,87 @@
+//
+//  LineGraphHistory.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 10/11/2017.
+//
+//
+
+#ifndef LineGraphHistory_h
+#define LineGraphHistory_h
+
+#include <stdio.h>
+#include "HistoryGraph.hpp"
+
+class LineGraphHistory : public HistoryGraph
+{
+public:
+    LineGraphHistory ( GraphState* state ) : HistoryGraph (state)
+    {
+        //
+    }
+    ~LineGraphHistory ( void )
+    {
+        //
+    }
+    void updateRep ( void )
+    {
+        //
+    }
+    void drawSubGraph ( std::string subLabel, DataContainer<std::list<double>>& data, ofRectangle subLabelRect )
+    {
+        double
+        minIn = 0,
+        minOut = 0,
+        maxOut = -subLabelRect.height,
+        startPosY = subLabelRect.height,
+        pointDistance = subLabelRect.width/data.labelData.size(),
+        separation = pointDistance/2;
+        //halfSeparation = separation/2;
+        bool drawZeroSep = false;
+        
+        if (data.minValue < 0)
+        { // Add negative part
+            startPosY = subLabelRect.height/2;
+            minIn = -data.maxValue;
+            minOut = subLabelRect.height/2;
+            maxOut /= 2;
+            drawZeroSep = true;
+        }
+        
+        ofBeginShape();
+        ofFill();
+        ofVertex(subLabelRect.x, subLabelRect.y + startPosY);
+        double output = mapVals(data.labelData.front(), minIn, data.maxValue, minOut, maxOut );
+        ofVertex(subLabelRect.x, subLabelRect.y + startPosY + output);
+        uint32_t i = 0;
+        for (double d : data.labelData)
+        {
+            output = mapVals(d, minIn, data.maxValue, minOut, maxOut );
+            ofSetColor (graphColor);
+            
+            ofVertex(subLabelRect.x + pointDistance * i + separation, subLabelRect.y + startPosY + output);
+            //ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
+            ++i;
+            
+        }
+        output = mapVals(data.labelData.back(), minIn, data.maxValue, minOut, maxOut );
+        ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY + output);
+        ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
+        ofEndShape();
+        
+        if (drawZeroSep)
+        {
+            ofSetLineWidth(1.25);
+            ofSetColor (175,150,150);
+            ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
+                       subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
+        }
+    }
+    void update ( void )
+    {
+        //
+    }
+};
+
+
+#endif /* LineGraphHistory_h */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.cpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.cpp
new file mode 100644
index 0000000..dc2bc83
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.cpp
@@ -0,0 +1,173 @@
+//
+//  RapidVisualization.cpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 13/11/2017.
+//
+//
+
+#include <stdio.h>
+#include "RapidVisualization.hpp"
+
+RapidVisualization::RapidVisualization ( void )
+{
+    
+}
+
+RapidVisualization::~RapidVisualization ( void )
+{
+    std::map<std::string, RapidVisualizer*>::iterator it;
+    for(it = visualizers.begin(); it != visualizers.end(); ++it)
+    {
+        if (it->second)
+            delete it->second;
+    }
+}
+
+void RapidVisualization::setup ( ofRectangle posAndSize, uint32_t defaultHistorySize )
+{
+    this->posAndSize = posAndSize;
+    this->defaultHistorySize = defaultHistorySize;
+}
+
+std::pair<std::string, std::string> RapidVisualization::getRoute(std::string& address)
+{
+    std::pair<std::string, std::string> ret;
+    size_t delimPos = address.substr(1).find("/");
+    if (delimPos != std::string::npos)
+    {
+        delimPos += 1;
+        ret.first = address.substr(0, delimPos);
+        ret.second = address.substr(delimPos);
+    }
+    else
+    {
+        ret.first = address.substr(0, address.length());
+        ret.second = "/";
+    }
+    return ret;
+}
+
+void RapidVisualization::addData ( std::string address, std::vector<double>& data )
+{
+    std::pair<std::string, std::string> route = getRoute(address);
+    if ( visualizers.find(route.first) == visualizers.end() ) {
+        RapidVisualizer* ptr = visualizers[route.first] = new RapidVisualizer(); // Add new graph
+        ptr->setup(route.first, RapidVisualizer::LINE_GRAPH_WITH_HISTORY, defaultHistorySize, Graph::GRAPHS_STACKED, false, ofRectangle(0,0,100,100));
+    }
+    
+    RapidVisualizer* rapidVizPtr = visualizers[route.first];
+    rapidVizPtr->addData(address, data);
+    updateRep();
+}
+
+void RapidVisualization::addData ( std::string graphName, std::string subGraphName, std::vector<double>& data )
+{
+    if ( visualizers.find(graphName) == visualizers.end() ) {
+        RapidVisualizer* ptr = visualizers[graphName] = new RapidVisualizer(); // Add new graph
+        ptr->setup(graphName, RapidVisualizer::LINE_GRAPH_WITH_HISTORY, defaultHistorySize, Graph::GRAPHS_STACKED, false, ofRectangle(0,0,100,100));
+    }
+    
+    RapidVisualizer* rapidVizPtr = visualizers[graphName];
+    rapidVizPtr->addData(subGraphName, data);
+    updateRep();
+}
+
+void RapidVisualization::reset ( void )
+{
+    std::map<std::string, RapidVisualizer*>::iterator it;
+    for(it = visualizers.begin(); it != visualizers.end(); ++it)
+    {
+        if (it->second)
+            delete it->second;
+    }
+    visualizers.clear();
+}
+
+void RapidVisualization::update ( void )
+{
+    uint32_t tempNumGraphs = 0;
+    bool resetCase = false; // This is terrible  (stops jitter when selecting new graph type)
+    std::map<std::string, RapidVisualizer*>::iterator it;
+    for(it = visualizers.begin(); it != visualizers.end(); ++it)
+    {
+        it->second->update();
+        uint32_t tGraph = it->second->getNumGraphs();
+        if (tGraph == 0)
+        {
+            resetCase = true;
+            break;
+        }
+        tempNumGraphs += tGraph;
+    }
+    if (tempNumGraphs != numGraphs && !resetCase)
+    {
+        numGraphs = tempNumGraphs;
+        updateRep();
+    }
+}
+
+void RapidVisualization::draw ( void )
+{
+    std::map<std::string, RapidVisualizer*>::iterator it;
+    for(it = visualizers.begin(); it != visualizers.end(); ++it)
+    {
+        it->second->draw();
+    }
+    if (!guiHidden)
+    for(it = visualizers.begin(); it != visualizers.end(); ++it)
+    {
+        it->second->drawMenu(posAndSize); // Stop menu being behind next graph
+    }
+    if (numGraphs == 0)
+    {
+        drawTextLabel("Waiting for data", ofVec2f(posAndSize.x+posAndSize.width/2, posAndSize.y), ofColor(24, 219, 92), ofColor(255, 157, 117), TextAlignment::CENTER, false );
+    }
+}
+
+void RapidVisualization::setPos ( ofVec2f pos )
+{
+    posAndSize.x = pos.x;
+    posAndSize.y = pos.y;
+    updateRep();
+}
+void RapidVisualization::setSize ( ofVec2f size )
+{
+    posAndSize.width = size.x;
+    posAndSize.height = size.y;
+    updateRep();
+}
+
+void RapidVisualization::setPosAndSize ( ofRectangle posAndSize )
+{
+    this->posAndSize = posAndSize;
+    updateRep();
+}
+
+void RapidVisualization::setHistorySize ( uint32_t size )
+{
+    defaultHistorySize = size;
+}
+
+void RapidVisualization::updateRep ( void )
+{
+    double graphHeight = posAndSize.height/numGraphs; // Height of single graph
+    std::map<std::string, RapidVisualizer*>::iterator it;
+    double drawHeight = posAndSize.y;
+    double height;
+    for(it = visualizers.begin(); it != visualizers.end(); ++it)
+    {
+        height = it->second->getNumGraphs()*graphHeight;
+        it->second->setRect(ofRectangle(posAndSize.x,
+                                       drawHeight,
+                                       posAndSize.width,
+                                       height));
+        
+        drawHeight += height;
+    }
+}
+
+void RapidVisualization::setGuiHidden ( bool guiHidden )
+{
+    this->guiHidden = guiHidden;
+}
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.hpp
new file mode 100644
index 0000000..5485e6c
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualization.hpp
@@ -0,0 +1,57 @@
+//
+//  RapidVisualization.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 10/11/2017.
+//
+//
+
+#ifndef RapidVisualization_h
+#define RapidVisualization_h
+
+#include "RapidVisualizer.hpp"
+
+class RapidVisualization
+{
+public:
+    RapidVisualization ( void );
+    ~RapidVisualization ( void );
+    
+    void setup ( ofRectangle posAndSize, uint32_t defaultHistorySize=256 );
+    
+    // Append multiple types of graphs to cycle through as new entry addresses are added
+    // Implement?
+    //RapidVisualizer* addGraphType ( RapidVisualizer::graphTypeRealtime graphType );
+    //RapidVisualizer* addGraphType ( RapidVisualizer::graphTypeWithHistory graphType, uint32_t historySize );
+    
+    void addData ( std::string address, std::vector<double>& data );
+    void addData ( std::string graphName, std::string subGraphName, std::vector<double>& data );
+    void reset ( void );
+    
+    void update ( void );
+    void draw ( void );
+    
+    void setPos ( ofVec2f pos );
+    void setSize ( ofVec2f size );
+    void setPosAndSize ( ofRectangle posAndSize );
+    
+    void setHistorySize ( uint32_t size );
+    void setGraphColor ( ofColor graphColor );
+    //void setGraphColor ( ofColor backgroundColor );
+    //void setGraphColor ( ofColor textColor );
+    
+    void setGuiHidden ( bool guiHidden );
+    
+private:
+    bool guiHidden = false;
+    uint32_t numGraphs = 0;
+    std::map<std::string, RapidVisualizer*> visualizers;
+    ofRectangle posAndSize;
+    
+    uint32_t defaultHistorySize;
+    
+    std::pair<std::string, std::string> getRoute ( std::string& address );
+    void updateRep ( void );
+};
+
+#endif /* RapidVisualization_h */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.cpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.cpp
new file mode 100644
index 0000000..1fe723f
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.cpp
@@ -0,0 +1,259 @@
+//
+//  RapidVisualizer.cpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 09/11/2017.
+//
+//
+
+#include "RapidVisualizer.hpp"
+
+RapidVisualizer::RapidVisualizer ( void )
+{
+    uint32 initialHistorySize = 256;
+    minimumGui.setup();
+    expandedGui.setBackgroundColor(ofColor(0,0,0,127));
+    expandedGui.setup();
+    
+    historySizeSlider.addListener(this, &RapidVisualizer::historySizeSliderChanged);
+    
+    auto gNameRef = expandButton.setup(graphState.label, false);
+    expandButton.setBackgroundColor(ofColor(0, 0, 0, 127));
+    minimumGui.add(gNameRef);
+
+    expandedGui.add(graphTypesLabel.setup("Graph Type",""));
+    expandedGui.add(viewTypes[0].setup("Bar Chart", false));
+    expandedGui.add(viewTypes[1].setup("Line Graph", false));
+    expandedGui.add(viewTypes[2].setup("Line Graph History", false));
+    expandedGui.add(viewTypes[3].setup("Scope", false));
+    expandedGui.add(prefLabel.setup("Preferences",""));
+    expandedGui.add(splitArrayIntoGraphs.setup("Split Multiple Input", false));
+    expandedGui.add(historySizeSlider.setup("History Size", initialHistorySize, 4, 4096));
+    
+    setHistory(initialHistorySize);
+    setLayout (Graph::GRAPHS_STACKED);
+    setSplitEachArgument (false);
+    setRect (ofRectangle(0,0,200,200));
+}
+
+RapidVisualizer::~RapidVisualizer ( void )
+{
+    if (currentGraph)
+    {
+        delete currentGraph;
+    }
+}
+
+void RapidVisualizer::setup ( std::string label, graphTypeT graphType, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize )
+{
+    
+    setLabel (label);
+    setLayout (layout);
+    setSplitEachArgument (splitEachArgument);
+    setRect (positionAndSize);
+    setGraphType (graphType); // FIXME: Check if successfully initialized
+}
+
+void RapidVisualizer::setup ( std::string label, graphTypeT graphType, uint32_t historySize, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize )
+{
+    setLabel (label);
+    setLayout (layout);
+    setSplitEachArgument (splitEachArgument);
+    setRect (positionAndSize);
+    setGraphType (graphType, historySize); // FIXME: Check if successfully initialized
+}
+
+void RapidVisualizer::setLabel ( std::string label )
+{
+    graphState.label = label;
+    expandButton.setName(label);
+    if (currentGraph)
+        currentGraph->updateRep();
+}
+
+void RapidVisualizer::setGraphType ( graphTypeT graphType )
+{
+    if (currentGraphType != graphType)
+    {
+        if (currentGraph)
+        {
+            // TODO: add lock for when doing this, or have all types loaded?
+            delete currentGraph;
+        }
+        switch (graphType) {
+            case BAR_CHART:
+                currentGraph = new BarChart ( &graphState );
+                graphState.hasHistory = false;
+                break;
+            case LINE_GRAPH:
+                currentGraph = new LineGraph ( &graphState );
+                graphState.hasHistory = false;
+                break;
+            case LINE_GRAPH_WITH_HISTORY:
+                currentGraph = new LineGraphHistory ( &graphState );
+                graphState.hasHistory = true;
+                break;
+            case SCOPE:
+                currentGraph = new ScopeWithHistory ( &graphState );
+                graphState.hasHistory = true;
+                break;
+            default:
+                break;
+        }
+        currentGraphType = graphType;
+    }
+}
+
+void RapidVisualizer::setGraphType ( graphTypeT graphType, uint32_t historySize )
+{
+    setHistory(historySize);
+    setGraphType(graphType);
+}
+
+void RapidVisualizer::setLayout ( Graph::graphLayout layout )
+{
+    graphState.layout = layout;
+    if (currentGraph)
+        currentGraph->updateRep();
+}
+
+void RapidVisualizer::setSplitEachArgument ( bool splitEachArgument )
+{
+    this->splitEachArgument = splitEachArgument;
+    splitArrayIntoGraphs = splitEachArgument;
+}
+
+void RapidVisualizer::setPos ( ofVec2f pos )
+{
+    graphState.positionAndSize.x = pos.x;
+    graphState.positionAndSize.y = pos.y;
+    if (currentGraph)
+        currentGraph->updateRep();
+}
+
+void RapidVisualizer::setSize ( ofVec2f size )
+{
+    graphState.positionAndSize.width = size.x;
+    graphState.positionAndSize.height = size.y;
+    if (currentGraph)
+        currentGraph->updateRep();
+}
+
+void RapidVisualizer::setRect ( ofRectangle positionAndSize )
+{
+    graphState.positionAndSize = positionAndSize;
+    if (currentGraph)
+        currentGraph->updateRep();
+}
+
+void RapidVisualizer::setHistory( uint32_t historySize )
+{
+    graphState.historySize = historySize;
+    historySizeSlider = historySize;
+    if (currentGraph)
+        currentGraph->updateRep();
+}
+
+void RapidVisualizer::historySizeSliderChanged (int32_t &val)
+{
+    setHistory(val);
+}
+
+void RapidVisualizer::addData ( std::string subLabel, std::vector<double>& data )
+{
+    if (currentGraph)
+    {
+        if (splitEachArgument && data.size() > 1)
+        {
+            int16_t currentIndex = 0;
+            std::vector<double> splitData;
+            for (double d : data)
+            {
+                splitData = {d};
+                currentGraph->addData (subLabel + ofToString(currentIndex), splitData);
+                ++currentIndex;
+            }
+        }
+        else
+            currentGraph->addData (subLabel, data);
+    }
+}
+
+void RapidVisualizer::update ( void )
+{
+    if (currentGraph)
+    {
+        currentGraph->update();
+    }
+}
+
+void RapidVisualizer::drawMenu ( ofRectangle mainPosAndSize )
+{
+    minimumGui.setPosition(graphState.positionAndSize.x, graphState.positionAndSize.y);
+    minimumGui.draw();
+    if (menuActive)
+    {
+        double expandedGuiXPos = graphState.positionAndSize.x + minimumGui.getWidth();
+        double expandedGuiYPos = graphState.positionAndSize.y;
+        double mainYPlusHeight = mainPosAndSize.y + mainPosAndSize.height;
+        double check = expandedGuiYPos + expandedGui.getHeight() - mainYPlusHeight;
+        
+        if (check > 0)
+        {
+            expandedGuiYPos -= check;
+        }
+        
+        expandedGui.setPosition(expandedGuiXPos, expandedGuiYPos);
+        expandedGui.draw();
+    }
+}
+
+void RapidVisualizer::draw ( void )
+{
+    
+    if (currentGraph)
+    {
+        currentGraph->draw();
+    }
+    //drawMenu();
+    menuActive = expandButton;
+    if (menuActive)
+    {
+        bool noneActive = true;
+        for (uint8_t i = 0; i < NUMVIEWTYPES; ++i)
+        {
+            if (viewTypes[i] && !oldTypeToggles[i])
+            {
+                std::fill(viewTypes, viewTypes + NUMVIEWTYPES, false);
+                viewTypes[i] = true;
+                setGraphType(static_cast<graphTypeT>(i));
+            }
+            if (viewTypes[i])
+                noneActive = false;
+            oldTypeToggles[i] = viewTypes[i];
+        }
+        if (noneActive && currentGraphType != NOT_INITIALIZED)
+        {
+            viewTypes[currentGraphType] = true;
+        }
+        if (splitEachArgument != splitArrayIntoGraphs)
+        {
+            // Dirty, solution for now
+            if (currentGraph)
+                currentGraph->reset();
+            splitEachArgument = splitArrayIntoGraphs;
+        }
+    }
+}
+
+uint32_t RapidVisualizer::getNumGraphs ( void ) const
+{
+    if (currentGraph)
+    {
+        return currentGraph->getNumSubGraphs();
+    } else {
+        return 0;
+    }
+}
+
+#undef NUMVIEWTYPES
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.hpp
new file mode 100644
index 0000000..0e4a9f2
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RapidVisualizer.hpp
@@ -0,0 +1,79 @@
+//
+//  RapidVisualizer.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 09/11/2017.
+//
+//
+
+#ifndef RapidVisualizer_hpp
+#define RapidVisualizer_hpp
+
+#include <stdio.h>
+#include <map>
+#include "ofMain.h"
+#include "Graph.hpp"
+#include "BarChart.hpp"
+#include "LineGraph.hpp"
+#include "LineGraphHistory.hpp"
+#include "ScopeWithHistory.hpp"
+
+#define NUMVIEWTYPES 4
+
+// TODO add historySize slider and init with default 256 or so
+
+class RapidVisualizer {
+public:
+    enum graphTypeT {
+        BAR_CHART,
+        LINE_GRAPH,
+        LINE_GRAPH_WITH_HISTORY,
+        SCOPE,
+        NOT_INITIALIZED
+    };
+    
+    RapidVisualizer ( void );
+    ~RapidVisualizer ( void );
+    
+    void setup ( std::string label, graphTypeT graphType, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize );
+    void setup ( std::string label, graphTypeT graphType, uint32_t historySize, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize );
+    
+    void setLabel ( std::string label );
+    void setGraphType ( graphTypeT graphType );
+    void setGraphType ( graphTypeT graphType, uint32_t historySize );
+    void setLayout ( Graph::graphLayout layout );
+    void setSplitEachArgument ( bool splitEachArgument );
+    void setPos ( ofVec2f pos );
+    void setSize ( ofVec2f size );
+    void setRect ( ofRectangle rect );
+    void setHistory ( uint32_t historySize );
+    void historySizeSliderChanged (int32_t &val);
+    
+    void addData ( std::string subLabel, std::vector<double>& data );
+    void update ( void );
+    void drawMenu ( ofRectangle mainPosAndSize );
+    void draw ( void );
+    
+    uint32_t getNumGraphs ( void ) const;
+    
+private:
+    graphTypeT currentGraphType = NOT_INITIALIZED;
+    Graph::GraphState graphState;
+    Graph* currentGraph = nullptr;
+    bool splitEachArgument = false;
+    bool menuActive = false;
+    
+    ofxLabel graphName;
+    ofxLabel graphTypesLabel;
+    ofxLabel prefLabel;
+    ofxPanel minimumGui;
+    ofxToggle expandButton;
+    ofxPanel expandedGui;
+    ofxIntSlider historySizeSlider;
+    ofxToggle splitArrayIntoGraphs;
+    
+    bool oldTypeToggles[NUMVIEWTYPES];
+    ofxToggle viewTypes[NUMVIEWTYPES];
+};
+
+#endif /* RapidVisualizer_hpp */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RealTimeGraph.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RealTimeGraph.hpp
new file mode 100644
index 0000000..99e928e
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/RealTimeGraph.hpp
@@ -0,0 +1,107 @@
+//
+//  RealTimeGraph.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 09/11/2017.
+//
+//
+
+#ifndef RealTimeGraph_h
+#define RealTimeGraph_h
+
+#include <map>
+#include <vector>
+#include <string>
+#include "Graph.hpp"
+
+class RealTimeGraph : public Graph
+{
+public:
+    RealTimeGraph ( GraphState* state )
+    : Graph(state)
+    {
+    }
+    ~RealTimeGraph ( void )
+    {
+    }
+    
+    void addData ( std::string subLabel, std::vector<double>& data )
+    {
+        if ( subLabelData.find(subLabel) == subLabelData.end() ) {
+            // not found
+            subLabelData[subLabel] = DataContainer<std::vector<double>>();
+        }
+        
+        subLabelData[subLabel].labelData = data;
+        subLabelData[subLabel].updateMinMax();
+    }
+    
+    virtual void reset ( void )
+    {
+        subLabelData.clear();
+    }
+    
+    virtual void updateRep ( void ) = 0; // update representation
+    virtual void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelrect ) = 0;
+    
+    virtual void update ( void ) = 0;
+    
+    void draw ( void )
+    {
+        uint32_t numElements = subLabelData.size();
+        uint16_t heightBetweenSubLabels = state->positionAndSize.height/numElements;
+        uint16_t subLabelY = 0;
+        
+        ofSetColor (255,255,255);
+        ofDrawLine(state->positionAndSize.x, state->positionAndSize.y,
+                   state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y);
+        
+        std::map<std::string, DataContainer<std::vector<double>>>::iterator it;
+        for(it = subLabelData.begin(); it != subLabelData.end(); ++it)
+        {
+            std::string subLabel = it->first;
+            DataContainer<std::vector<double>>& data = it->second;
+            
+            drawSubGraph (subLabel, data, ofRectangle(state->positionAndSize.x,
+                                                           state->positionAndSize.y + subLabelY,
+                                                           state->positionAndSize.width,
+                                                           heightBetweenSubLabels));
+            // Draw label and background
+            drawTextLabel(subLabel, ofVec2f(state->positionAndSize.x + state->positionAndSize.width,
+                                            state->positionAndSize.y + subLabelY),
+                          ofColor(100, 100, 100), ofColor(textColor), TextAlignment::RIGHT, false);
+            
+            
+            // Draw max value
+            drawTextLabel(ofToString(data.maxValue),
+                          ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
+                                            state->positionAndSize.y + subLabelY),
+                          ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, false);
+
+            // Increment Y position
+            subLabelY += heightBetweenSubLabels;
+            
+            // Draw min value
+            // Could show actually found min value
+            drawTextLabel(ofToString((data.minValue < 0) ? -data.maxValue : 0),
+                          ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
+                                  state->positionAndSize.y + subLabelY),
+                          ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, true);
+            
+            // Draw Line at bottom of graph
+            ofSetLineWidth(2.0);
+            ofSetColor (180,180,180);
+            ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + subLabelY,
+                       state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + subLabelY);
+        }
+    }
+    
+    uint32_t getNumSubGraphs ( void ) const
+    {
+        return subLabelData.size();
+    }
+protected:
+    std::map <std::string, DataContainer<std::vector<double>>> subLabelData;
+};
+
+#endif /* RealTimeGraph_h */
diff --git a/examples/ofx/Bitalino_rapidmix/rapidVisualizer/ScopeWithHistory.hpp b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/ScopeWithHistory.hpp
new file mode 100644
index 0000000..60ab23e
--- /dev/null
+++ b/examples/ofx/Bitalino_rapidmix/rapidVisualizer/ScopeWithHistory.hpp
@@ -0,0 +1,184 @@
+//
+//  ScopeWithHistory.hpp
+//  RapidVisualizerOSC
+//
+//  Created by James on 12/11/2017.
+//
+//
+
+#ifndef ScopeWithHistory_h
+#define ScopeWithHistory_h
+
+#include <stdio.h>
+#include "Graph.hpp"
+
+class ScopeWithHistory : public Graph
+{
+public:
+    ScopeWithHistory ( GraphState* state ) : Graph (state)
+    {
+        //
+    }
+    ~ScopeWithHistory ( void )
+    {
+        //
+    }
+    void addData ( std::string subLabel, std::vector<double>& data )
+    {
+        if (data.size() < state->historySize)
+        {
+            //FIXME: can be sped up by using the result of this search instead of accessing by[] again
+            if ( subLabelData.find(subLabel) == subLabelData.end() ) {
+                // not found
+                DataContainer<std::vector<double>> container;
+                container.labelData.resize(state->historySize);
+                std::fill(container.labelData.begin(), container.labelData.end(), 0.0);
+                subLabelData[subLabel] = container;
+            }
+            DataContainer<std::vector<double>>&  dataRef = subLabelData[subLabel];
+            std::vector<double>& referencedList = dataRef.labelData;
+            while (referencedList.size() >= state->historySize)
+            {
+                referencedList.pop_back();
+            }
+            for (int32_t i = data.size()-1; i >= 0; --i)
+            {
+                double val = data[i];
+                if (val < dataRef.minValue)
+                    dataRef.minValue = val;
+                if (val > dataRef.maxValue)
+                    dataRef.maxValue = val;
+                if (fabs(val) > dataRef.maxValue)
+                    dataRef.maxValue = fabs(val);
+                if (referencedList.size() + data.size() >= state->historySize)
+                {
+                    referencedList[dataRef.iteratorPos] = val;
+                    dataRef.iteratorPos = (dataRef.iteratorPos + data.size()) % state->historySize;
+                } else {
+                    referencedList.insert(referencedList.begin(), val);
+                }
+            }
+        }
+    }
+    void updateRep ( void )
+    {
+        //
+    }
+    void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelRect )
+    {
+        double
+        minIn = 0,
+        minOut = 0,
+        maxOut = -subLabelRect.height,
+        startPosY = subLabelRect.height,
+        pointDistance = subLabelRect.width/data.labelData.size(),
+        separation = pointDistance/2;
+        //halfSeparation = separation/2;
+        bool drawZeroSep = false;
+        
+        if (data.minValue < 0)
+        { // Add negative part
+            startPosY = subLabelRect.height/2;
+            minIn = -data.maxValue;
+            minOut = subLabelRect.height/2;
+            maxOut /= 2;
+            drawZeroSep = true;
+        }
+        
+        ofBeginShape();
+        ofFill();
+        ofVertex(subLabelRect.x, subLabelRect.y + startPosY);
+        double output = mapVals(data.labelData.front(), minIn, data.maxValue, minOut, maxOut );
+        ofVertex(subLabelRect.x, subLabelRect.y + startPosY + output);
+        uint32_t i = 0;
+        for (double d : data.labelData)
+        {
+            output = mapVals(d, minIn, data.maxValue, minOut, maxOut );
+            ofSetColor (graphColor);
+            
+            ofVertex(subLabelRect.x + pointDistance * i + separation, subLabelRect.y + startPosY + output);
+            //ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
+            ++i;
+            
+        }
+        output = mapVals(data.labelData.back(), minIn, data.maxValue, minOut, maxOut );
+        ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY + output);
+        ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
+        ofEndShape();
+        
+        if (drawZeroSep)
+        {
+            ofSetLineWidth(1.25);
+            ofSetColor (175,150,150);
+            ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
+                       subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
+        }
+    }
+    void update ( void )
+    {
+        //
+    }
+    
+    void reset ( void )
+    {
+        subLabelData.clear();
+    }
+    
+    void draw ( void )
+    {
+        uint32_t numElements = subLabelData.size();
+        uint16_t heightBetweenSubLabels = state->positionAndSize.height/numElements;
+        uint16_t subLabelY = 0;
+        
+        ofSetColor (255,255,255);
+        ofDrawLine(state->positionAndSize.x, state->positionAndSize.y,
+                   state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y);
+        
+        std::map<std::string, DataContainer<std::vector<double>>>::iterator it;
+        for(it = subLabelData.begin(); it != subLabelData.end(); ++it)
+        {
+            std::string subLabel = it->first;
+            DataContainer<std::vector<double>>& data = it->second;
+            
+            drawSubGraph (subLabel, data, ofRectangle(state->positionAndSize.x,
+                                                      state->positionAndSize.y + subLabelY,
+                                                      state->positionAndSize.width,
+                                                      heightBetweenSubLabels));
+            // Draw label and background
+            drawTextLabel(subLabel, ofVec2f(state->positionAndSize.x + state->positionAndSize.width,
+                                            state->positionAndSize.y + subLabelY),
+                          ofColor(100, 100, 100), ofColor(textColor), TextAlignment::RIGHT, false);
+            
+            
+            // Draw max value
+            drawTextLabel(ofToString(data.maxValue),
+                          ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
+                                  state->positionAndSize.y + subLabelY),
+                          ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, false);
+            
+            // Increment Y position
+            subLabelY += heightBetweenSubLabels;
+            
+            // Draw min value
+            // Show actual min value?
+            drawTextLabel(ofToString((data.minValue < 0) ? -data.maxValue : 0),
+                          ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
+                                  state->positionAndSize.y + subLabelY),
+                          ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, true);
+            
+            // Draw Line at bottom of graph
+            ofSetLineWidth(2.0);
+            ofSetColor (180,180,180);
+            ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + subLabelY,
+                       state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + subLabelY);
+        }
+    }
+    uint32_t getNumSubGraphs ( void ) const
+    {
+        return subLabelData.size();
+    }
+protected:
+    std::map <std::string, DataContainer<std::vector<double>>> subLabelData;
+};
+
+#endif /* ScopeWithHistory_h */
-- 
GitLab