diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ad035d6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,30 @@ +--- +IndentWidth: 4 +UseTab: Never +SpacesInParentheses: true +ColumnLimit: 100 +--- +Language: Cpp + +BreakBeforeBraces: Allman +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AlignAfterOpenBracket: AlwaysBreak +BinPackArguments: false +BinPackParameters: false + +DerivePointerAlignment: false +PointerAlignment: Left + +Cpp11BracedListStyle: false + +AllowAllConstructorInitializersOnNextLine: true +BreakConstructorInitializers: BeforeColon +ConstructorInitializerAllOnOneLineOrOnePerLine: true + +AllowShortFunctionsOnASingleLine: Empty +--- +Language: Java +# TODO +--- diff --git a/.gitignore b/.gitignore index cfbf241..722d5e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -plotter/LICENSE -plotter/README.md \ No newline at end of file +.vscode diff --git a/README.md b/README.md index 0950c5e..9782070 100644 --- a/README.md +++ b/README.md @@ -30,16 +30,16 @@ Plotter p; // create plotter void setup() { - p.Begin(); // start plotter + p.begin(); // start plotter - p.AddTimeGraph( "Some title of a graph", 1500, "label for x", x ); // add any graphs you want + p.lineGraph( "Some title of a graph", 1500, "label for x", x ); // add any graphs you want } void loop() { - x = 10*sin( 2.0*PI*( millis() / 5000.0 ) ); // update your variables like usual + x = 10*sin( 2.0*PI*( millis() / 5000.0 ) ); // update your variables like usual - p.Plot(); // plot all current data -- usually called within loop() + p.plot(); // plot all current data -- usually called within loop() } ``` diff --git a/examples/control_points_displayed/control_points_displayed.ino b/examples/control_points_displayed/control_points_displayed.ino index d4f7453..3cbc4cf 100644 --- a/examples/control_points_displayed/control_points_displayed.ino +++ b/examples/control_points_displayed/control_points_displayed.ino @@ -11,7 +11,7 @@ #include "Plotter.h" -// Plotted variables must be declared as globals +// Plotted variables must be declared as globals double x; double y; @@ -21,24 +21,23 @@ Plotter p; void setup() { // Start plotter - p.Begin(); - - // Add X-Y graphs - p.AddXYGraph( "X-Y graph w/ 500 points", 500, "x axis", x, "y axis", y ); - p.AddXYGraph( "X-Y graph w/ 200 points", 200, "x axis", x, "y axis", y ); + p.begin(); - // Add time graphs. Notice the effect of points displayed on the time scale - p.AddTimeGraph( "Time graph w/ 500 points", 500, "x label", x ); - p.AddTimeGraph( "Time graph w/ 200 points", 200, "x label", x ); + // Add X-Y graphs + p.scatterGraph( "X-Y graph w/ 500 points", 500, "x axis", x, "y axis", y ); + p.scatterGraph( "X-Y graph w/ 200 points", 200, "x axis", x, "y axis", y ); + // Add time graphs. Notice the effect of points displayed on the time scale + p.lineGraph( "Time graph w/ 500 points", 500, "x label", x ); + p.lineGraph( "Time graph w/ 200 points", 200, "x label", x ); } void loop() { // Update variables with arbitrary sine/cosine data - x = 10*sin( 2.0*PI*( millis() / 5000.0 ) ); - y = 10*cos( 2.0*PI*( millis() / 5000.0 ) ); + x = 10 * sin( 2.0 * PI * ( millis() / 5000.0 ) ); + y = 10 * cos( 2.0 * PI * ( millis() / 5000.0 ) ); // Plot - p.Plot(); + p.plot(); } diff --git a/examples/different_types/different_types.ino b/examples/different_types/different_types.ino index 8cf37b9..8c00461 100644 --- a/examples/different_types/different_types.ino +++ b/examples/different_types/different_types.ino @@ -11,7 +11,7 @@ #include "Plotter.h" -// Plotted variables must be declared as globals +// Plotted variables must be declared as globals float f; float f2; int i; @@ -23,23 +23,22 @@ Plotter p; void setup() { // Start plotter - p.Begin(); - - // Add time graphs. - p.AddTimeGraph( "float vs int", 500, "float", f, "int", i ); - p.AddTimeGraph( "float vs char", 500, "float", f2, "unsigned char", ch ); + p.begin(); + // Add time graphs. + p.lineGraph( "float vs int", 500, "float", f, "int", i ); + p.lineGraph( "float vs char", 500, "float", f2, "unsigned char", ch ); } void loop() { // Update different variable types with arbitrary sine/cosine data unsigned long time = millis(); - f = 5*sin( 2.0*PI*( time / 5000.0 ) ); - i = 5*sin( 2.0*PI*( time / 5000.0 ) ); - f2 = 300*sin( 2.0*PI*( time / 5000.0 ) ); - ch = 300*sin( 2.0*PI*( time / 5000.0 ) ); + f = 5 * sin( 2.0 * PI * ( time / 5000.0 ) ); + i = 5 * sin( 2.0 * PI * ( time / 5000.0 ) ); + f2 = 300 * sin( 2.0 * PI * ( time / 5000.0 ) ); + ch = 300 * sin( 2.0 * PI * ( time / 5000.0 ) ); // Plot - p.Plot(); + p.plot(); } diff --git a/examples/dynamically_modify_graph_layout/dynamically_modify_graph_layout.ino b/examples/dynamically_modify_graph_layout/dynamically_modify_graph_layout.ino index 7bd45db..95bd041 100644 --- a/examples/dynamically_modify_graph_layout/dynamically_modify_graph_layout.ino +++ b/examples/dynamically_modify_graph_layout/dynamically_modify_graph_layout.ino @@ -11,7 +11,7 @@ #include "Plotter.h" -// Plotted variables must be declared as globals +// Plotted variables must be declared as globals double x; double y; double z; @@ -27,37 +27,40 @@ boolean remove_graph = true; void setup() { // Plotter constructor - p.Begin(); - + p.begin(); + // Add a graph during setup - p.AddTimeGraph( "x and y against time", 1000, "x label", x, "y label", y ); + p.lineGraph( "x and y against time", 1000, "x label", x, "y label", y ); } void loop() { // After 3 seconds add a 1-variable graph - if ( millis() > 3000 && add_first ) { - p.AddTimeGraph( "First dynamic addition", 1000, "z label", z ); - add_first = false; + if ( millis() > 3000 && add_first ) + { + p.lineGraph( "First dynamic addition", 1000, "z label", z ); + add_first = false; } // After 5 seconds, add a 3-variable graph - if ( millis() > 5000 && add_second ) { - p.AddTimeGraph( "Second dynamic addition", 1000, "x label", x, "y label", y, "z label", z ); - add_second = false; + if ( millis() > 5000 && add_second ) + { + p.lineGraph( "Second dynamic addition", 1000, "x label", x, "y label", y, "z label", z ); + add_second = false; } // After 10 seconds, remove the 1-variable graph - if ( millis() > 10000 && remove_graph ) { - p.Remove( 1 ); // (an index of 1 will remove the second graph added) - remove_graph = false; + if ( millis() > 10000 && remove_graph ) + { + p.remove( 1 ); // (an index of 1 will remove the second graph added) + remove_graph = false; } - + // Update x, y and z variables with arbitrary sine/cosine data - x = 3*sin( 2.0*PI*( millis() / 2000.0 ) ); - y = 2*cos( 2.0*PI*( millis() / 2000.0 ) ); - z = 1*sin( 2.0*PI*( millis() / 2000.0 ) ); - - // Plot - p.Plot(); -} \ No newline at end of file + x = 3 * sin( 2.0 * PI * ( millis() / 2000.0 ) ); + y = 2 * cos( 2.0 * PI * ( millis() / 2000.0 ) ); + z = 1 * sin( 2.0 * PI * ( millis() / 2000.0 ) ); + + // Plot + p.plot(); +} diff --git a/examples/multivariable_plotting/multivariable_plotting.ino b/examples/multivariable_plotting/multivariable_plotting.ino index dbb3266..4326952 100644 --- a/examples/multivariable_plotting/multivariable_plotting.ino +++ b/examples/multivariable_plotting/multivariable_plotting.ino @@ -11,7 +11,7 @@ #include "Plotter.h" -// Plotted variables must be declared as globals +// Plotted variables must be declared as globals double v; double w; double x; @@ -24,22 +24,26 @@ Plotter p; void setup() { // Start plotter - p.Begin(); - + p.begin(); + // Add 5 variable time graph - p.AddTimeGraph( "5 variable time graph", 1000, "v label", v, "w label", w, "x label", x, - "y label", y, "z label", z ); + LineGraph& g = p.lineGraph( "5 variable time graph" ); + g.line( "v label", v ); + g.line( "w label", w ); + g.line( "x label", x ); + g.line( "y label", y ); + g.line( "z label", z ); } void loop() { // Update variables with arbitrary sine/cosine data - v = 3*cos( 2.0*PI*( millis()/2500.0 ) ); + v = 3 * cos( 2.0 * PI * ( millis() / 2500.0 ) ); w = 4.0; - x = 10*sin( 2.0*PI*( millis() / 5000.0 ) ); - y = 7*cos( 2.0*PI*( millis() / 5000.0 ) ); - z = 5*sin( 2.0*PI*( millis() / 5000.0 ) ); + x = 10 * sin( 2.0 * PI * ( millis() / 5000.0 ) ); + y = 7 * cos( 2.0 * PI * ( millis() / 5000.0 ) ); + z = 5 * sin( 2.0 * PI * ( millis() / 5000.0 ) ); // Plot - p.Plot(); + p.plot(); } diff --git a/examples/quick_start/quick_start.ino b/examples/quick_start/quick_start.ino index 5980929..de3d550 100644 --- a/examples/quick_start/quick_start.ino +++ b/examples/quick_start/quick_start.ino @@ -17,13 +17,14 @@ Plotter p; void setup() { - p.Begin(); + p.begin(); - p.AddTimeGraph( "Some title of a graph", 1500, "label for x", x ); + p.lineGraph( "Some title of a graph", 1500, "label for x", x ); } -void loop() { - x = 10*sin( 2.0*PI*( millis() / 5000.0 ) ); +void loop() +{ + x = 10 * sin( 2.0 * PI * ( millis() / 5000.0 ) ); - p.Plot(); // usually called within loop() -} \ No newline at end of file + p.plot(); // usually called within loop() +} diff --git a/examples/set_colors/set_colors.ino b/examples/set_colors/set_colors.ino deleted file mode 100644 index cac274e..0000000 --- a/examples/set_colors/set_colors.ino +++ /dev/null @@ -1,40 +0,0 @@ -/* - =========================================================================================== - Example to demonstrate configuring specific colors for each variable - ------------------------------------------------------------------------------------------- - Plotter - v2.4.1 - https://github.com/deviaconley/arduino-plotter - by Devin Conley - =========================================================================================== -*/ - -#include "Plotter.h" - -double x; -double y; - -Plotter p; - -void setup() -{ - // Start plotter - p.Begin(); - - // Add plots - p.AddTimeGraph( "Some title of a graph", 500, "label for x", x, "label for y", y ); - p.AddXYGraph( "Title of X vs Y graph", 1000, "x axis", x, "y axis", y ); - - // Set variable colors of graph with index 0 to pink and orange - p.SetColor( 0, "pink", "orange" ); - // Set color of x vs y graph at index 1 to cyan - p.SetColor( 1, "cyan" ); - -} - -void loop() { - x = 0.0009*sin( 2.0*PI*( millis() / 5000.0 ) ); - y = 90000*cos( 2.0*PI*( millis() / 5000.0 ) ); - - p.Plot(); // usually called within loop() -} \ No newline at end of file diff --git a/examples/set_optionals/set_optionals.ino b/examples/set_optionals/set_optionals.ino new file mode 100644 index 0000000..370b32c --- /dev/null +++ b/examples/set_optionals/set_optionals.ino @@ -0,0 +1,42 @@ +/* + =========================================================================================== + Example to demonstrate configuring specific colors for each variable + ------------------------------------------------------------------------------------------- + Plotter + v2.4.1 + https://github.com/deviaconley/arduino-plotter + by Devin Conley + =========================================================================================== +*/ + +#include "Plotter.h" + +double x; +double y; + +Plotter p; + +void setup() +{ + // Start plotter + p.begin(); + + // Add plots + LineGraph& lg = p.lineGraph( "Some title of a graph", 500 ); + ScatterGraph& sg = p.scatterGraph( "Title of X vs Y graph", 1000 ); + + // Add line variables and specify color + lg.line( "label for x", x, "pink" ); + lg.line( "label for y", y, "orange" ); + + // Add scatter channel and specify color + sg.scatter( "x axis", x, "y axis", y, "cyan" ); +} + +void loop() +{ + x = 0.0009 * sin( 2.0 * PI * ( millis() / 5000.0 ) ); + y = 90000 * cos( 2.0 * PI * ( millis() / 5000.0 ) ); + + p.plot(); // usually called within loop() +} diff --git a/listener/Graph.java b/listener/Graph.java index 0b0c143..0714ad0 100644 --- a/listener/Graph.java +++ b/listener/Graph.java @@ -53,155 +53,155 @@ class Graph // Contructor public Graph( PApplet parent, float posY, float posX, float height, float width, - boolean xvy, int numVars, int maxPoints, - String title, String[] labels, int[] colors ) + boolean xvy, int numVars, int maxPoints, + String title, String[] labels, int[] colors ) { - this.parent = parent; - this.posY = posY; - this.posX = posX; - this.height = height; - this.width = width; - this.xvy = xvy; - this.numVars = numVars; - this.maxPoints = maxPoints; - this.title = title; - this.labels = labels; - this.colors = colors; + this.parent = parent; + this.posY = posY; + this.posX = posX; + this.height = height; + this.width = width; + this.xvy = xvy; + this.numVars = numVars; + this.maxPoints = maxPoints; + this.title = title; + this.labels = labels; + this.colors = colors; - // this.parent.println( "Constructed new graph: ", this.title, " at ", this.posY, " ", this.posX ); + // this.parent.println( "Constructed new graph: ", this.title, " at ", this.posY, " ", this.posX ); - // Initialize - this.index = 0; - this.data = new double[maxPoints][numVars][2]; - this.extremesCounter = new int[4]; - this.extremes = new double[4]; - this.currPoints = 0; + // Initialize + this.index = 0; + this.data = new double[maxPoints][numVars][2]; + this.extremesCounter = new int[4]; + this.extremes = new double[4]; + this.currPoints = 0; } // Modifiers public void Reconfigure( float posY, float posX, float height, float width, - boolean xvy, int numVars, int maxPoints, - String title, String[] labels ) + boolean xvy, int numVars, int maxPoints, + String title, String[] labels ) { - this.posY = posY; - this.posX = posX; - this.height = height; - this.width = width; - this.xvy = xvy; - this.numVars = numVars; - this.maxPoints = maxPoints; - this.title = title; - this.labels = labels; + this.posY = posY; + this.posX = posX; + this.height = height; + this.width = width; + this.xvy = xvy; + this.numVars = numVars; + this.maxPoints = maxPoints; + this.title = title; + this.labels = labels; } public void Reconfigure( float posY, float posX, float height, float width ) { - this.posY = posY; - this.posX = posX; - this.height = height; - this.width = width; + this.posY = posY; + this.posX = posX; + this.height = height; + this.width = width; } public void Update( double[] newData, int time ) { - // Store data - if ( this.xvy ) - { - // Validate - if ( newData.length != 2 ) - { - //this.parent.println( "Invalid data passed to X v. Y graph." ); - return; - } + // Store data + if ( this.xvy ) + { + // Validate + if ( newData.length != 2 ) + { + //this.parent.println( "Invalid data passed to X v. Y graph." ); + return; + } - this.data[this.index][0][0] = newData[0]; - this.data[this.index][0][1] = newData[1]; - } - else - { - // Validate - if ( newData.length != this.numVars ) - { - //this.parent.println( "Invalid data passed to time graph." ); - return; - } - - for ( int i = 0; i < this.numVars; i++ ) - { - this.data[this.index][i][0] = time; - this.data[this.index][i][1] = newData[i]; - } - } - - // Counter for num points defined - if ( this.currPoints < this.maxPoints ) - { - this.currPoints++; - } + this.data[this.index][0][0] = newData[0]; + this.data[this.index][0][1] = newData[1]; + } + else + { + // Validate + if ( newData.length != this.numVars ) + { + //this.parent.println( "Invalid data passed to time graph." ); + return; + } + + for ( int i = 0; i < this.numVars; i++ ) + { + this.data[this.index][i][0] = time; + this.data[this.index][i][1] = newData[i]; + } + } + + // Counter for num points defined + if ( this.currPoints < this.maxPoints ) + { + this.currPoints++; + } - // Check extremes - this.CheckExtremes(); - - // Advance index position and rollback if needed - this.index++; - if ( this.index >= this.maxPoints ) - { - this.index = 0; - } + // Check extremes + this.CheckExtremes(); + + // Advance index position and rollback if needed + this.index++; + if ( this.index >= this.maxPoints ) + { + this.index = 0; + } } public void Plot() { - // Plot Background - this.parent.fill( PLOT_COL ); - this.parent.stroke( PLOT_COL ); - this.parent.strokeWeight( (this.width + this.height) / 2.0f * PT_SZ ); - this.parent.rect( this.posX, this.posY, this.width, this.height ); - - // Title - this.parent.textSize( (int)( (this.width + this.height) / 2.0f * TITLE_SZ ) ); - this.parent.fill( 255 ); - this.parent.textAlign( this.parent.CENTER, this.parent.TOP ); - this.parent.text( this.title, this.posX + this.width / 2, this.posY + 10 ); + // Plot Background + this.parent.fill( PLOT_COL ); + this.parent.stroke( PLOT_COL ); + this.parent.strokeWeight( (this.width + this.height) / 2.0f * PT_SZ ); + this.parent.rect( this.posX, this.posY, this.width, this.height ); + + // Title + this.parent.textSize( (int)( (this.width + this.height) / 2.0f * TITLE_SZ ) ); + this.parent.fill( 255 ); + this.parent.textAlign( this.parent.CENTER, this.parent.TOP ); + this.parent.text( this.title, this.posX + this.width / 2, this.posY + 10 ); - // Calculations for offset and scaling of graph ( vs. time ) - double xScale = this.width / ( this.extremes[1] - this.extremes[0] ); - double xOffset = xScale * this.extremes[0]; - double yScale = AXIS_COV * this.height / ( this.extremes[3] - this.extremes[2] ); - double yOffset = yScale * this.extremes[3] + 0.5 * ( 1.0 - AXIS_COV ) * this.height; - - // Modify scaling and offset - if ( this.xvy ) - { - xScale *= AXIS_COV; - xOffset = xScale * this.extremes[0] - 0.5 * ( 1.0 - AXIS_COV ) * this.width; - } + // Calculations for offset and scaling of graph ( vs. time ) + double xScale = this.width / ( this.extremes[1] - this.extremes[0] ); + double xOffset = xScale * this.extremes[0]; + double yScale = AXIS_COV * this.height / ( this.extremes[3] - this.extremes[2] ); + double yOffset = yScale * this.extremes[3] + 0.5 * ( 1.0 - AXIS_COV ) * this.height; + + // Modify scaling and offset + if ( this.xvy ) + { + xScale *= AXIS_COV; + xOffset = xScale * this.extremes[0] - 0.5 * ( 1.0 - AXIS_COV ) * this.width; + } // Do actual data plotting - for ( int i = 0; i < this.numVars; i++ ) - { - this.parent.stroke( this.colors[i] ); - for ( int j = 0; j < this.currPoints; j++ ) - { - this.parent.point( (float)(this.posX + (this.data[j][i][0]*xScale - xOffset)), - (float)(this.posY + yOffset - data[j][i][1]*yScale) ); - } - } + for ( int i = 0; i < this.numVars; i++ ) + { + this.parent.stroke( this.colors[i] ); + for ( int j = 0; j < this.currPoints; j++ ) + { + this.parent.point( (float)(this.posX + (this.data[j][i][0]*xScale - xOffset)), + (float)(this.posY + yOffset - data[j][i][1]*yScale) ); + } + } - // X vs Y and vs Time specific stuff - if ( this.xvy ) - { - this.DrawXYStuff(); - } - else - { - this.DrawTimeStuff(); - } - - // Draw Ticks - this.DrawTicks( xScale, xOffset, yScale, yOffset ); + // X vs Y and vs Time specific stuff + if ( this.xvy ) + { + this.DrawXYStuff(); + } + else + { + this.DrawTimeStuff(); + } + + // Draw Ticks + this.DrawTicks( xScale, xOffset, yScale, yOffset ); } @@ -209,198 +209,198 @@ public void Plot() private void DrawTimeStuff() { - int labelSz = (int) ( (this.width + this.height) / 2.0f * LABEL_SZ ); + int labelSz = (int) ( (this.width + this.height) / 2.0f * LABEL_SZ ); - // Setup legend start - float textPos = this.posY + labelSz; - this.parent.textAlign( this.parent.RIGHT, this.parent.TOP ); - this.parent.textSize( labelSz ); - - // Draw each legend entry - for ( int i = 0; i < this.numVars; i++ ) - { - this.parent.fill( this.colors[i] ); - this.parent.text( this.labels[i], this.posX + this.width - 10, textPos); - textPos += ( labelSz + labelSz/4 ); - this.parent.stroke( this.colors[i] ); - } + // Setup legend start + float textPos = this.posY + labelSz; + this.parent.textAlign( this.parent.RIGHT, this.parent.TOP ); + this.parent.textSize( labelSz ); + + // Draw each legend entry + for ( int i = 0; i < this.numVars; i++ ) + { + this.parent.fill( this.colors[i] ); + this.parent.text( this.labels[i], this.posX + this.width - 10, textPos); + textPos += ( labelSz + labelSz/4 ); + this.parent.stroke( this.colors[i] ); + } } private void DrawXYStuff() { - // X and Y labels - this.parent.textSize( (int)( (this.width + this.height) / 2.0f * LABEL_SZ) ); - this.parent.textAlign( this.parent.LEFT, this.parent.TOP ); - this.parent.text( this.labels[1], this.posX + 10, this.posY + 10); + // X and Y labels + this.parent.textSize( (int)( (this.width + this.height) / 2.0f * LABEL_SZ) ); + this.parent.textAlign( this.parent.LEFT, this.parent.TOP ); + this.parent.text( this.labels[1], this.posX + 10, this.posY + 10); - this.parent.textAlign( this.parent.RIGHT, this.parent.BOTTOM ); - this.parent.text( this.labels[0], this.posX + this.width - 10, this.posY + this.height - 3.5f*TICK_LEN); + this.parent.textAlign( this.parent.RIGHT, this.parent.BOTTOM ); + this.parent.text( this.labels[0], this.posX + this.width - 10, this.posY + this.height - 3.5f*TICK_LEN); } private void DrawTicks( double xScale, double xOffset, double yScale, double yOffset ) { - // Label graph with numbered tick marks - this.parent.stroke( 255 ); - this.parent.fill( 255 ); - this.parent.textSize( (int)( ( this.width + this.height ) / 2.0f * NUM_SZ ) ); - this.parent.textAlign( this.parent.LEFT, this.parent.CENTER ); - - // Draw ticks along y-axis - float tempX = this.posX - TICK_LEN / 2; - float tickOffset = 0.5f * ( 1.0f - AXIS_COV ) * this.height; - float tickInterval = AXIS_COV * this.height / (NUM_TICKS - 1); - for ( float tempY = this.posY + tickOffset; tempY <= this.posY + this.height - tickOffset; - tempY += tickInterval ) - { - float val = (float) ( ( ( yOffset + this.posY ) - (double)tempY ) / yScale ); - String fmt = GetNumberFormat( val ); - this.parent.line( tempX, tempY, tempX + TICK_LEN, tempY ); - this.parent.text( String.format( fmt, val ), tempX + TICK_LEN + 5, tempY ); - } - - // x-axis - this.parent.textAlign( this.parent.CENTER, this.parent.BOTTOM ); - float tempY = this.posY + this.height - TICK_LEN / 2; - tickOffset = 0.5f * ( 1.0f - AXIS_COV ) * this.width; - tickInterval = AXIS_COV * this.width / (NUM_TICKS - 1); - for ( tempX = this.posX + tickOffset; tempX <= this.posX + this.width - tickOffset; - tempX += tickInterval ) - { - float val = (float) ( ( (double)tempX + xOffset - this.posX ) / xScale ); - this.parent.line( tempX, tempY, tempX, tempY + TICK_LEN ); - if ( this.xvy ) - { - String fmt = GetNumberFormat( val ); - this.parent.text( String.format( fmt, val ), tempX, tempY - 5 ); - } - else - { - this.parent.text( String.format( "%d", (int)val ), tempX, tempY - 5 ); - } - } + // Label graph with numbered tick marks + this.parent.stroke( 255 ); + this.parent.fill( 255 ); + this.parent.textSize( (int)( ( this.width + this.height ) / 2.0f * NUM_SZ ) ); + this.parent.textAlign( this.parent.LEFT, this.parent.CENTER ); + + // Draw ticks along y-axis + float tempX = this.posX - TICK_LEN / 2; + float tickOffset = 0.5f * ( 1.0f - AXIS_COV ) * this.height; + float tickInterval = AXIS_COV * this.height / (NUM_TICKS - 1); + for ( float tempY = this.posY + tickOffset; tempY <= this.posY + this.height - tickOffset; + tempY += tickInterval ) + { + float val = (float) ( ( ( yOffset + this.posY ) - (double)tempY ) / yScale ); + String fmt = GetNumberFormat( val ); + this.parent.line( tempX, tempY, tempX + TICK_LEN, tempY ); + this.parent.text( String.format( fmt, val ), tempX + TICK_LEN + 5, tempY ); + } + + // x-axis + this.parent.textAlign( this.parent.CENTER, this.parent.BOTTOM ); + float tempY = this.posY + this.height - TICK_LEN / 2; + tickOffset = 0.5f * ( 1.0f - AXIS_COV ) * this.width; + tickInterval = AXIS_COV * this.width / (NUM_TICKS - 1); + for ( tempX = this.posX + tickOffset; tempX <= this.posX + this.width - tickOffset; + tempX += tickInterval ) + { + float val = (float) ( ( (double)tempX + xOffset - this.posX ) / xScale ); + this.parent.line( tempX, tempY, tempX, tempY + TICK_LEN ); + if ( this.xvy ) + { + String fmt = GetNumberFormat( val ); + this.parent.text( String.format( fmt, val ), tempX, tempY - 5 ); + } + else + { + this.parent.text( String.format( "%d", (int)val ), tempX, tempY - 5 ); + } + } } private String GetNumberFormat( float value ) { - int n = SIG_DIGITS; - int d = 1; - while ( n > 0 && Math.round( Math.abs( value / d ) ) > 0 ) - { - n--; - d *= 10; - } - - String fmt = "%" + Integer.toString( 1 + SIG_DIGITS - n ) + "." + Integer.toString( n ); - if ( ( Math.abs( value ) > 1000 || Math.abs( value ) < 0.001 ) && value != 0 ) - { - fmt += "e"; - } - else - { - fmt += "f"; - } - return fmt; + int n = SIG_DIGITS; + int d = 1; + while ( n > 0 && Math.round( Math.abs( value / d ) ) > 0 ) + { + n--; + d *= 10; + } + + String fmt = "%" + Integer.toString( 1 + SIG_DIGITS - n ) + "." + Integer.toString( n ); + if ( ( Math.abs( value ) > 1000 || Math.abs( value ) < 0.001 ) && value != 0 ) + { + fmt += "e"; + } + else + { + fmt += "f"; + } + return fmt; } private void CheckExtremes() { - // Check new values - this.CompareToExtremes( this.index ); + // Check new values + this.CompareToExtremes( this.index ); - // Time extremes - if ( ! this.xvy ) - { - // Get index of oldest data point - int oldest = this.index + 1; - if ( oldest >= this.currPoints ) - { - oldest = 0; - } + // Time extremes + if ( ! this.xvy ) + { + // Get index of oldest data point + int oldest = this.index + 1; + if ( oldest >= this.currPoints ) + { + oldest = 0; + } - if ( this.currPoints < this.maxPoints ) - { - // Estimate lower extreme - this.extremes[0] = this.data[this.index][0][0] - - ( this.data[this.index][0][0] - this.data[oldest][0][0] ) - * ( (double)this.maxPoints / (double)this.currPoints ); - } - else - { - // Normally just take oldest - this.extremes[0] = this.data[oldest][0][0]; - } - this.extremesCounter[0] = 0; - this.extremes[1] = this.data[this.index][0][0]; - this.extremesCounter[1] = 0; - } + if ( this.currPoints < this.maxPoints ) + { + // Estimate lower extreme + this.extremes[0] = this.data[this.index][0][0] + - ( this.data[this.index][0][0] - this.data[oldest][0][0] ) + * ( (double)this.maxPoints / (double)this.currPoints ); + } + else + { + // Normally just take oldest + this.extremes[0] = this.data[oldest][0][0]; + } + this.extremesCounter[0] = 0; + this.extremes[1] = this.data[this.index][0][0]; + this.extremesCounter[1] = 0; + } // Check for extremes going out of scope - boolean recalc = false; - for ( int i = 0; i < 4; i++ ) - { - this.extremesCounter[i]++; - recalc |= this.extremesCounter[i] > this.maxPoints; - } + boolean recalc = false; + for ( int i = 0; i < 4; i++ ) + { + this.extremesCounter[i]++; + recalc |= this.extremesCounter[i] > this.maxPoints; + } - if ( ! recalc ) - { - return; - } + if ( ! recalc ) + { + return; + } - // this.parent.println("Recalculating extremes..."); + // this.parent.println("Recalculating extremes..."); - // Full re-calculation for new extremes - if ( this.xvy ) - { - this.extremes[0] = this.data[0][0][0]; // (x-min) - this.extremesCounter[0] = 0; - this.extremes[1] = this.data[0][0][0]; // (x-max) - this.extremesCounter[1] = 0; - } - this.extremes[2] = this.data[0][0][1]; // (y-min) - this.extremesCounter[2] = 0; - this.extremes[3] = this.data[0][0][1]; // (y-max) - this.extremesCounter[3] = 0; + // Full re-calculation for new extremes + if ( this.xvy ) + { + this.extremes[0] = this.data[0][0][0]; // (x-min) + this.extremesCounter[0] = 0; + this.extremes[1] = this.data[0][0][0]; // (x-max) + this.extremesCounter[1] = 0; + } + this.extremes[2] = this.data[0][0][1]; // (y-min) + this.extremesCounter[2] = 0; + this.extremes[3] = this.data[0][0][1]; // (y-max) + this.extremesCounter[3] = 0; - for ( int i = 0; i < this.currPoints; i++ ) - { - this.CompareToExtremes( i ); - } + for ( int i = 0; i < this.currPoints; i++ ) + { + this.CompareToExtremes( i ); + } } private void CompareToExtremes( int i ) // i : dataIndex { - for ( int j = 0; j < this.numVars; j++ ) - { - // Y max/min - if ( this.data[i][j][1] < this.extremes[2] ) // (min) - { - this.extremes[2] = this.data[i][j][1]; - this.extremesCounter[2] = 0; - } - else if ( this.data[i][j][1] > this.extremes[3] ) // (max) - { - this.extremes[3] = this.data[i][j][1]; - this.extremesCounter[3] = 0; - } - // X max/min - if ( this.xvy ) - { - if ( this.data[i][j][0] < this.extremes[0] ) // (min) - { - this.extremes[0] = this.data[i][j][0]; - this.extremesCounter[0] = 0; - } - else if ( this.data[i][j][0] > this.extremes[1] ) // (max) - { - this.extremes[1] = this.data[i][j][0]; - this.extremesCounter[1] = 0; - } - } - } + for ( int j = 0; j < this.numVars; j++ ) + { + // Y max/min + if ( this.data[i][j][1] < this.extremes[2] ) // (min) + { + this.extremes[2] = this.data[i][j][1]; + this.extremesCounter[2] = 0; + } + else if ( this.data[i][j][1] > this.extremes[3] ) // (max) + { + this.extremes[3] = this.data[i][j][1]; + this.extremesCounter[3] = 0; + } + // X max/min + if ( this.xvy ) + { + if ( this.data[i][j][0] < this.extremes[0] ) // (min) + { + this.extremes[0] = this.data[i][j][0]; + this.extremesCounter[0] = 0; + } + else if ( this.data[i][j][0] > this.extremes[1] ) // (max) + { + this.extremes[1] = this.data[i][j][0]; + this.extremesCounter[1] = 0; + } + } + } } // Constants diff --git a/src/Graph.cpp b/src/Graph.cpp new file mode 100644 index 0000000..9092875 --- /dev/null +++ b/src/Graph.cpp @@ -0,0 +1,148 @@ +/* + =========================================================================================== + Graph classes + ------------------------------------------------------------------------------------------- + Plotter + v2.4.1 + https://github.com/devinaconley/arduino-plotter + by Devin Conley + =========================================================================================== +*/ + +#include "Graph.h" +#include "Arduino.h" + +// Graph + +Graph::Graph( const char* title, int maxPoints ) + : next( nullptr ), + title( title ), + maxPoints( maxPoints ), + size( 0 ), + head( nullptr ), + tail( nullptr ) +{ +} + +Graph::~Graph() +{ + Variable* temp = head; + Variable* tempNext; + while ( temp->next ) + { + tempNext = temp->next; + delete temp; + temp = tempNext; + } + delete temp; +} + +void Graph::addVariable( Variable* var ) +{ + if ( head ) + { + tail->next = var; + tail = var; + } + else + { + head = var; + tail = var; + } + size++; +} + +void Graph::plot( bool config ) +{ + Serial.print( "{" ); + + if ( config ) + { + Serial.print( "\"" ), Serial.print( TITLE_KEY ), Serial.print( "\":" ); + Serial.print( "\"" ), Serial.print( title ), Serial.print( "\"" ); + Serial.print( ",\"" ), Serial.print( XVY_KEY ); + Serial.print( "\":" ), Serial.print( xvy() ); + Serial.print( ",\"" ), Serial.print( POINTS_DISPLAYED_KEY ); + Serial.print( "\":" ), Serial.print( maxPoints ); + Serial.print( ",\"" ), Serial.print( SIZE_KEY ); + Serial.print( "\":" ), Serial.print( size ); + Serial.print( ",\"" ), Serial.print( LABELS_KEY ), Serial.print( "\":[" ); + Variable* temp = head; + while ( temp ) + { + Serial.print( "\"" ), Serial.print( temp->getLabel() ), Serial.print( "\"" ); + temp = temp->next; + if ( temp ) + { + Serial.print( "," ); + } + } + Serial.print( "],\"" ), Serial.print( COLORS_KEY ), Serial.print( "\":[" ); + temp = head; + while ( temp ) + { + Serial.print( "\"" ), Serial.print( temp->getColor() ), Serial.print( "\"" ); + temp = temp->next; + if ( temp ) + { + Serial.print( "," ); + } + } + Serial.print( "]," ); + } + + Serial.print( "\"" ), Serial.print( DATA_KEY ), Serial.print( "\":[" ); + Variable* temp = head; + while ( temp ) + { + Serial.print( temp->getValue(), 8 ); + temp = temp->next; + if ( temp ) + { + Serial.print( "," ); + } + } + Serial.print( "]}" ); +} + +// Line graph + +LineGraph::LineGraph( const char* title = "", int range = 5000, int maxPoints = 1000 ) + : Graph( title, maxPoints ), range( range ) +{ +} + +// Scatter graph + +ScatterGraph::ScatterGraph( const char* title = "", int maxPoints = 1000 ) + : Graph( title, maxPoints ) +{ +} + +// Variable + +Graph::Variable::Variable() : ref( nullptr ), deref( nullptr ), next( nullptr ) {} + +Graph::Variable::Variable( + const char* label, + void* ref, + double ( *deref )( void* ), + const char* color ) + : label( label ), color( color ), ref( ref ), deref( deref ), next( nullptr ) +{ +} + +const char* Graph::Variable::getLabel() +{ + return label; +} + +double Graph::Variable::getValue() +{ + return deref( ref ); +} + +const char* Graph::Variable::getColor() +{ + return color; +} diff --git a/src/Graph.h b/src/Graph.h new file mode 100644 index 0000000..8264c0f --- /dev/null +++ b/src/Graph.h @@ -0,0 +1,114 @@ +/* + =========================================================================================== + Graph classes + ------------------------------------------------------------------------------------------- + Plotter + v2.4.1 + https://github.com/devinaconley/arduino-plotter + by Devin Conley + =========================================================================================== +*/ + +#ifndef GRAPH_H +#define GRAPH_H + +#include "Util.h" + +// Abstract graph class / node +class Graph +{ + public: + Graph( const char* title, int maxPoints ); + ~Graph(); + virtual void plot( bool config ); + + // Data + Graph* next; + + protected: + // Nested Variable wrapper class + class Variable + { + public: + Variable(); + Variable( const char* label, void* ref, double ( *deref )( void* ), const char* color ); + + const char* getLabel(); + double getValue(); + const char* getColor(); + + Variable* next; + + private: + // Data + const char* label; + const char* color; + void* ref; + double ( *deref )( void* ); + + }; //-- Variable + + template static double dereference( void* ref ) + { + return static_cast( ( *static_cast( ref ) ) ); + } + + protected: + void addVariable( Variable* variable ); + virtual bool xvy() = 0; + int size; + int maxPoints; + const char* title; + Variable* head; + Variable* tail; +}; + +// Line graph vs time +class LineGraph : public Graph +{ + public: + LineGraph( const char* title = "", int range = 5000, int maxPoints = 1000 ); + + template void line( const char* label, T& ref, const char* color = "" ) + { + if ( color[0] == '\0' ) + { + color = COLORS[size % 6]; + } + addVariable( new Variable( label, static_cast( &ref ), &dereference, color ) ); + } + + virtual bool xvy() + { + return false; + } + + private: + // Data + int range; +}; + +// X vs. Y graph +class ScatterGraph : public Graph +{ + public: + ScatterGraph( const char* title = "", int maxPoints = 1000 ); + + template + void scatter( const char* labelX, X& refX, const char* labelY, Y& refY, const char* color = "" ) + { + if ( color[0] == '\0' ) + { + color = COLORS[( size / 2 ) % 6]; + } + addVariable( new Variable( labelX, static_cast( &refX ), &dereference, color ) ); + addVariable( new Variable( labelY, static_cast( &refY ), &dereference, color ) ); + } + + virtual bool xvy() + { + return true; + } +}; + +#endif diff --git a/src/Plotter.cpp b/src/Plotter.cpp index 4926548..d2373cf 100644 --- a/src/Plotter.cpp +++ b/src/Plotter.cpp @@ -1,18 +1,18 @@ /* =========================================================================================== Plotter is an Arduino library that allows easy multi-variable and multi-graph plotting. The - library supports plots against time as well as 2-variable "X vs Y" graphing. + library supports plots against time as well as 2-variable "X vs Y" graphing. ------------------------------------------------------------------------------------------- - The library stores and handles all relevant graph information and variable references, + The library stores and handles all relevant graph information and variable references, and transfers information via the serial port to a listener program written with the software provided by Processing. No modification is needed to this program; graph placement, - axis-scaling, etc. are handled automatically. - Multiple options for this listener are available including stand-alone applications as well + axis-scaling, etc. are handled automatically. + Multiple options for this listener are available including stand-alone applications as well as the source Processing script. - The library, these listeners, a quick-start guide, documentation, and usage examples are + The library, these listeners, a quick-start guide, documentation, and usage examples are available at: - + https://github.com/devinaconley/arduino-plotter ------------------------------------------------------------------------------------------- @@ -24,288 +24,118 @@ */ #include "Plotter.h" +#include "Arduino.h" // Plotter Plotter::Plotter() { - head = NULL; - tail = NULL; + head = nullptr; + tail = nullptr; numGraphs = 0; counter = 0; lastUpdated = millis(); } -void Plotter::Begin() +void Plotter::begin() { Serial.begin( 115200 ); lastUpdated = millis(); } Plotter::~Plotter() -{ - Graph * temp = head; - Graph * tempNext; +{ + Graph* temp = head; + Graph* tempNext; while ( temp->next ) { - tempNext = temp->next; - delete temp; - temp = tempNext; + tempNext = temp->next; + delete temp; + temp = tempNext; } delete temp; } -void Plotter::AddGraphHelper( const char * title, VariableWrapper * wrappers, int sz, bool xvy, int pointsDisplayed ) -{ - Graph * temp = new Graph( title, wrappers, sz, xvy, pointsDisplayed ); +void Plotter::addGraph( Graph* graph ) +{ if ( head ) { - tail->next = temp; - tail = temp; + tail->next = graph; + tail = graph; } else { - head = temp; - tail = temp; + head = graph; + tail = graph; } - - numGraphs++; + + numGraphs++; lastUpdated = millis(); } -bool Plotter::Remove( int index ) +bool Plotter::remove( int index ) { if ( numGraphs == 0 || index < 0 || numGraphs <= index ) { - return false; + return false; } else { - Graph * temp = head; - if ( index == 0 ) - { - head = head->next; - delete temp; - } - else - { - Graph * last = temp; - for ( int i = 0; i < index; i++ ) - { - last = temp; - temp = temp->next; - } - last->next = temp->next; - numGraphs--; - delete temp; - } - lastUpdated = millis(); - return true; - } -} - -bool Plotter::SetColor( int index, const char * colorA ) -{ - const char * colors[] = { colorA }; - return SetColorHelper( index, 1, colors ); -} - -bool Plotter::SetColor( int index, const char * colorA, const char * colorB ) -{ - const char * colors[] = { colorA, colorB }; - return SetColorHelper( index, 2, colors ); -} - -bool Plotter::SetColor( int index, const char * colorA, const char * colorB, const char * colorC ) -{ - const char * colors[] = { colorA, colorB, colorC }; - return SetColorHelper( index, 3, colors ); -} - -bool Plotter::SetColor( int index, const char * colorA, const char * colorB, const char * colorC, - const char * colorD ) -{ - const char * colors[] = { colorA, colorB, colorC, colorD }; - return SetColorHelper( index, 4, colors ); -} - -bool Plotter::SetColor( int index, const char * colorA, const char * colorB, const char * colorC, - const char * colorD, const char * colorE ) -{ - const char * colors[] = { colorA, colorB, colorC, colorD, colorE }; - return SetColorHelper( index, 5, colors ); -} - -bool Plotter::SetColor( int index, const char * colorA, const char * colorB, const char * colorC, - const char * colorD, const char * colorE, const char * colorF ) -{ - const char * colors[] = { colorA, colorB, colorC, colorD, colorE, colorF }; - return SetColorHelper( index, 6, colors ); -} - -bool Plotter::SetColorHelper( int index, int sz, const char * * colors ) -{ - if ( numGraphs == 0 || index < 0 || numGraphs <= index ) - { - return false; - } - Graph * temp = head; - for ( int i = 0; i < index; i++ ) - { - temp = temp->next; - } - bool res = temp->SetColor( sz, colors ); - if ( res ) - { - lastUpdated = millis(); - } - return res; -} - -void Plotter::Plot() + Graph* temp = head; + if ( index == 0 ) + { + head = head->next; + delete temp; + } + else + { + Graph* last = temp; + for ( int i = 0; i < index; i++ ) + { + last = temp; + temp = temp->next; + } + last->next = temp->next; + numGraphs--; + delete temp; + } + lastUpdated = millis(); + return true; + } +} + +void Plotter::plot() { bool config = counter == 0; - Serial.print( "{\"" ); Serial.print( TIME_KEY ); Serial.print( "\":" ); Serial.print( millis() ); - + Serial.print( "{\"" ), Serial.print( TIME_KEY ); + Serial.print( "\":" ), Serial.print( millis() ); + if ( config ) { - Serial.print( ",\"" ); Serial.print( NUM_GRAPH_KEY ); Serial.print( "\":" ); Serial.print( numGraphs ); - Serial.print( ",\"" ); Serial.print( LAST_UPDATED_KEY ); Serial.print( "\":" ); Serial.print( lastUpdated ); + Serial.print( ",\"" ), Serial.print( NUM_GRAPH_KEY ); + Serial.print( "\":" ), Serial.print( numGraphs ); + Serial.print( ",\"" ), Serial.print( LAST_UPDATED_KEY ); + Serial.print( "\":" ), Serial.print( lastUpdated ); } - - Serial.print( ",\"" ); Serial.print( GRAPHS_KEY ); Serial.print( "\":[" ); - Graph * temp = head; + Serial.print( ",\"" ), Serial.print( GRAPHS_KEY ), Serial.print( "\":[" ); + + Graph* temp = head; while ( temp ) { - temp->Plot( config ); - temp = temp->next; - if ( temp ) - { - Serial.print( "," ); - } + temp->plot( config ); + temp = temp->next; + if ( temp ) + { + Serial.print( "," ); + } } - Serial.print( "]}" ); Serial.println( OUTER_KEY ); + Serial.print( "]}" ), Serial.println( OUTER_KEY ); counter++; if ( counter >= CONFIG_INTERVAL ) { - counter = 0; + counter = 0; } } - -// Graph - -Plotter::Graph::Graph( const char * title, VariableWrapper * wrappers, int size, bool xvy, int pointsDisplayed ) : - next( NULL ), - xvy( xvy ), - size( size ), - pointsDisplayed( pointsDisplayed ), - title( title ), - wrappers( wrappers ) -{} - -Plotter::Graph::~Graph() -{ - delete[] wrappers; -} - -void Plotter::Graph::Plot( bool config ) -{ - Serial.print( "{" ); - - if ( config ) - { - Serial.print( "\"" ); Serial.print( TITLE_KEY ); Serial.print( "\":" ); Serial.print( "\"" ); Serial.print( title ); Serial.print( "\"" ); - Serial.print( ",\"" ); Serial.print( XVY_KEY ); Serial.print( "\":" ); Serial.print( xvy ); - Serial.print( ",\"" ); Serial.print( POINTS_DISPLAYED_KEY ); Serial.print( "\":" ); Serial.print( pointsDisplayed ); - Serial.print( ",\"" ); Serial.print( SIZE_KEY ); Serial.print( "\":" ); Serial.print( size ); - Serial.print( ",\"" ); Serial.print( LABELS_KEY ); Serial.print( "\":[" ); - for ( int i = 0; i < size; i++ ) - { - Serial.print( "\"" ); Serial.print( wrappers[i].GetLabel() ); Serial.print( "\"" ); - if ( i + 1 < size ) - { - Serial.print( "," ); - } - } - Serial.print( "],\"" ); Serial.print( COLORS_KEY ); Serial.print( "\":[" ); - for ( int i = 0; i < size; i++ ) - { - Serial.print( "\"" ); Serial.print( wrappers[i].GetColor() ); Serial.print( "\"" ); - if ( i + 1 < size ) - { - Serial.print( "," ); - } - } - Serial.print( "]," ); - } - - Serial.print( "\"" ); Serial.print( DATA_KEY ); Serial.print( "\":[" ); - for ( int i = 0; i < size; i++ ) - { - Serial.print( wrappers[i].GetValue(), 8 ); - if ( i + 1 < size ) - { - Serial.print( "," ); - } - } - - Serial.print( "]}" ); -} - -bool Plotter::Graph::SetColor( int sz, const char * * colors ) -{ - if ( sz != size && !xvy ) - { - return false; - } - - if ( xvy ) - { - wrappers[0].SetColor( colors[0] ); - } - else - { - for ( int i = 0; i < size; i++ ) - { - wrappers[i].SetColor( colors[i] ); - } - } - return true; -} - -// VariableWrapper - -Plotter::VariableWrapper::VariableWrapper() : - ref( NULL ), - deref( NULL ) -{} - -Plotter::VariableWrapper::VariableWrapper( const char * label, void * ref, double ( * deref )( void * ), const char * color ) : - label( label ), - color( color ), - ref( ref ), - deref( deref ) -{} - -const char * Plotter::VariableWrapper::GetLabel() -{ - return label; -} - -double Plotter::VariableWrapper::GetValue() -{ - return deref( ref ); -} - -const char * Plotter::VariableWrapper::GetColor() -{ - return color; -} - -void Plotter::VariableWrapper::SetColor( const char * col ) -{ - color = col; -} diff --git a/src/Plotter.h b/src/Plotter.h index 4ed673b..a0cb9a9 100644 --- a/src/Plotter.h +++ b/src/Plotter.h @@ -1,18 +1,18 @@ /* =========================================================================================== Plotter is an Arduino library that allows easy multi-variable and multi-graph plotting. The - library supports plots against time as well as 2-variable "X vs Y" graphing. + library supports plots against time as well as 2-variable "X vs Y" graphing. ------------------------------------------------------------------------------------------- - The library stores and handles all relevant graph information and variable references, + The library stores and handles all relevant graph information and variable references, and transfers information via the serial port to a listener program written with the software provided by Processing. No modification is needed to this program; graph placement, - axis-scaling, etc. are handled automatically. - Multiple options for this listener are available including stand-alone applications as well + axis-scaling, etc. are handled automatically. + Multiple options for this listener are available including stand-alone applications as well as the source Processing script. - The library, these listeners, a quick-start guide, documentation, and usage examples are + The library, these listeners, a quick-start guide, documentation, and usage examples are available at: - + https://github.com/devinaconley/arduino-plotter ------------------------------------------------------------------------------------------- @@ -26,68 +26,107 @@ #ifndef PLOTTER_H #define PLOTTER_H -#include "Arduino.h" +#include "Graph.h" +#include "Util.h" class Plotter { -public: + public: // The constructor for Plotter requires no arguments Plotter(); // Initialize Plotter - void Begin(); - - /* - Add a 1-variable graph vs. time - + void begin(); + + /* + Create and add an empty line graph vs. time + Args: - title: const char * with title of graph - - pointsDisplayed: number of points to be shown at a given time. Used to control time-scaling + - maxPoints: number of points to be shown at a given time. Used to control time-scaling + + Returns: + - reference to created line graph + */ + LineGraph& lineGraph( const char* title, int maxPoints = 1000 ) + { + LineGraph* graph = new LineGraph( title, 5000, maxPoints ); + addGraph( graph ); + return *graph; + } + + /* + Create and add an empty scatter graph + + Args: + - title: const char * with title of graph + - maxPoints: max number of points to be shown at a given time. Controls data persistence + + Returns: + - reference to created scatter graph + */ + ScatterGraph& scatterGraph( const char* title, int maxPoints ) + { + ScatterGraph* graph = new ScatterGraph( title, maxPoints ); + addGraph( graph ); + return *graph; + } + + /* + Create and add a 1-variable graph vs. time + + Args: + - title: const char * with title of graph + - maxPoints: number of points to be shown at a given time. Used to control time-scaling - labelA: const char * with label of the plotted variable - refA: reference to global variable that will be updated throughout program - - Similar methods for multi-variable graphing vs. time are declared below and follow the same format + + Similar methods for multi-variable graphing vs. time are declared below and follow the same + format */ template - void AddTimeGraph( const char * title, int pointsDisplayed, - const char * labelA, A & refA ) + void lineGraph( const char* title, int maxPoints, const char* labelA, A& refA ) { - VariableWrapper * wrappers = new VariableWrapper[1]; - wrappers[0] = VariableWrapper( labelA, static_cast( &refA ), &Dereference, "green" ); - AddGraphHelper( title, wrappers, 1, false, pointsDisplayed ); + LineGraph* graph = new LineGraph( title, 5000, maxPoints ); + graph->line( labelA, refA ); + addGraph( graph ); } - + /* - Add an X vs. Y graph - + Create and add an X vs. Y scatter graph + Args: - title: const char * with title of graph - - pointsDisplayed: number of points to be shown at a given time. Determines duration of data persistance + - maxPoints: max number of points to be shown at a given time. Controls data persistence - labelX: const char * with label of variable to be plotted along X-axis - refX: reference to global X-variable that will be updated throughout program - labelY: const char * with label of variable to be plotted along Y-axis - refY: reference to global Y-variable that will be updated throughout program */ template - void AddXYGraph( const char * title, int pointsDisplayed, - const char * labelX, X & refX, const char * labelY, Y & refY ) + void scatterGraph( + const char* title, + int maxPoints, + const char* labelX, + X& refX, + const char* labelY, + Y& refY ) { - VariableWrapper * wrappers = new VariableWrapper[2]; - wrappers[0] = VariableWrapper( labelX, static_cast( &refX ), &Dereference, "green" ); - wrappers[1] = VariableWrapper( labelY, static_cast( &refY ), &Dereference, "green" ); - AddGraphHelper( title, wrappers, 2, true, pointsDisplayed ); + ScatterGraph* graph = new ScatterGraph( title, maxPoints ); + graph->scatter( labelX, refX, labelY, refY ); + addGraph( graph ); } - - /* + + /* Plot data - - Function to be called in order to send current values of all global variables to listener application. This - function will update all plots that have been added. - It is recommended to call plot() at the end of your loop function. + Function to be called in order to send current values of all global variables to listener + application. This function will update all plots that have been added. + + It is recommended to call plot() at the end of your loop function. */ - void Plot(); - + void plot(); + /* Remove Graph @@ -96,182 +135,56 @@ class Plotter Returns: - true, if successful */ - bool Remove( int index ); + bool remove( int index ); - /* - Set Variable Colors - - Args: - - index: position of graph to set colors for - - colorA: new color to set - Returns: - - true, if successful - */ - bool SetColor( int index, const char * colorA ); - - // Add a 2-variable graph vs. time + // Create and add a 2-variable graph vs. time template - void AddTimeGraph( const char * title, int pointsDisplayed, - const char * labelA, A & refA, const char * labelB, B & refB ) + void lineGraph( + const char* title, + int maxPoints, + const char* labelA, + A& refA, + const char* labelB, + B& refB ) { - VariableWrapper * wrappers = new VariableWrapper[2]; - wrappers[0] = VariableWrapper( labelA, static_cast( &refA ), &Dereference, "green" ); - wrappers[1] = VariableWrapper( labelB, static_cast( &refB ), &Dereference, "orange" ); - AddGraphHelper( title, wrappers, 2, false, pointsDisplayed ); + LineGraph* graph = new LineGraph( title, 5000, maxPoints ); + graph->line( labelA, refA ); + graph->line( labelB, refB ); + addGraph( graph ); } - // Add a 3-variable graph vs. time + // Create and add a 3-variable graph vs. time template - void AddTimeGraph( const char * title, int pointsDisplayed, - const char * labelA, A & refA, const char * labelB, B & refB, const char * labelC, C & refC ) - { - VariableWrapper * wrappers = new VariableWrapper[3]; - wrappers[0] = VariableWrapper( labelA, static_cast( &refA ), &Dereference, "green" ); - wrappers[1] = VariableWrapper( labelB, static_cast( &refB ), &Dereference, "orange" ); - wrappers[2] = VariableWrapper( labelC, static_cast( &refC ), &Dereference, "cyan" ); - AddGraphHelper( title, wrappers, 3, false, pointsDisplayed ); - } - - // Add a 4-variable graph vs. time - template - void AddTimeGraph( const char * title, int pointsDisplayed, - const char * labelA, A & refA, const char * labelB, B & refB, const char * labelC, C & refC, - const char * labelD, D & refD ) - { - VariableWrapper * wrappers = new VariableWrapper[4]; - wrappers[0] = VariableWrapper( labelA, static_cast( &refA ), &Dereference, "green" ); - wrappers[1] = VariableWrapper( labelB, static_cast( &refB ), &Dereference, "orange" ); - wrappers[2] = VariableWrapper( labelC, static_cast( &refC ), &Dereference, "cyan" ); - wrappers[3] = VariableWrapper( labelD, static_cast( &refD ), &Dereference, "yellow" ); - AddGraphHelper( title, wrappers, 4, false, pointsDisplayed ); - } - - // Add a 5-variable graph vs. time - template - void AddTimeGraph( const char * title, int pointsDisplayed, - const char * labelA, A & refA, const char * labelB, B & refB, const char * labelC, C & refC, - const char * labelD, D & refD, const char * labelE, E & refE ) - { - VariableWrapper * wrappers = new VariableWrapper[5]; - wrappers[0] = VariableWrapper( labelA, static_cast( &refA ), &Dereference, "green" ); - wrappers[1] = VariableWrapper( labelB, static_cast( &refB ), &Dereference, "orange" ); - wrappers[2] = VariableWrapper( labelC, static_cast( &refC ), &Dereference, "cyan" ); - wrappers[3] = VariableWrapper( labelD, static_cast( &refD ), &Dereference, "yellow" ); - wrappers[4] = VariableWrapper( labelE, static_cast( &refE ), &Dereference, "pink" ); - AddGraphHelper( title, wrappers, 5, false, pointsDisplayed ); - } - - // Add a 6-variable graph vs. time - template - void AddTimeGraph( const char * title, int pointsDisplayed, - const char * labelA, A & refA, const char * labelB, B & refB, const char * labelC, C & refC, - const char * labelD, D & refD, const char * labelE, E & refE, const char * labelF, F & refF ) + void lineGraph( + const char* title, + int maxPoints, + const char* labelA, + A& refA, + const char* labelB, + B& refB, + const char* labelC, + C& refC ) { - VariableWrapper * wrappers = new VariableWrapper[6]; - wrappers[0] = VariableWrapper( labelA, static_cast( &refA ), &Dereference, "green" ); - wrappers[1] = VariableWrapper( labelB, static_cast( &refB ), &Dereference, "orange" ); - wrappers[2] = VariableWrapper( labelC, static_cast( &refC ), &Dereference, "cyan" ); - wrappers[3] = VariableWrapper( labelD, static_cast( &refD ), &Dereference, "yellow" ); - wrappers[4] = VariableWrapper( labelE, static_cast( &refE ), &Dereference, "pink" ); - wrappers[5] = VariableWrapper( labelF, static_cast( &refF ), &Dereference, "blue" ); - AddGraphHelper( title, wrappers, 6, false, pointsDisplayed ); + LineGraph* graph = new LineGraph( title, 5000, maxPoints ); + graph->line( labelA, refA ); + graph->line( labelB, refB ); + graph->line( labelC, refC ); + addGraph( graph ); } - // Set Colors for multivariable graphs - bool SetColor( int index, const char * colorA, const char * colorB ); - bool SetColor( int index, const char * colorA, const char * colorB, const char * colorC ); - bool SetColor( int index, const char * colorA, const char * colorB, const char * colorC, - const char * colorD ); - bool SetColor( int index, const char * colorA, const char * colorB, const char * colorC, - const char * colorD, const char * colorE ); - bool SetColor( int index, const char * colorA, const char * colorB, const char * colorC, - const char * colorD, const char * colorE, const char * colorF ); - // Destructor for Plotter class ~Plotter(); -public: - - // Nested VariableWrapper class - class VariableWrapper - { - public: - VariableWrapper(); - VariableWrapper( const char * label, void * ref, double ( * deref )( void * ), const char * color ); - - const char * GetLabel(); - double GetValue(); - const char * GetColor(); - void SetColor( const char * col ); - - private: - // Data - const char * label; - const char * color; - void * ref; - double ( * deref )( void * ); - - }; //-- VariableWrapper - - -public: - // Nested Graph node class - class Graph - { - public: - Graph( const char * title, VariableWrapper * wrappers, int size, bool xvy, int pointsDisplayed ); - ~Graph(); - void Plot( bool config ); - bool SetColor( int sz, const char * * colors ); - - // Data - Graph * next; - - private: - bool xvy; - int size; - int pointsDisplayed; - const char * title; - VariableWrapper * wrappers; - - }; //-- Graph - -private: + private: // Helpers - void AddGraphHelper( const char * title, VariableWrapper * wrappers, int sz, bool xvy, int pointsDisplayed ); - bool SetColorHelper( int index, int sz, const char * * colors ); - - template - static double Dereference( void * ref ) - { - return static_cast( ( * static_cast( ref ) ) ); - } - - + void addGraph( Graph* graph ); + // Data int numGraphs; unsigned long lastUpdated; int counter; - Graph * head; - Graph * tail; - + Graph* head; + Graph* tail; }; //-- Plotter -// Constants -static const int CONFIG_INTERVAL = 50; - -// Transmission Keys -static const char * OUTER_KEY = "#"; -static const char * TIME_KEY = "t"; -static const char * NUM_GRAPH_KEY = "ng"; -static const char * LAST_UPDATED_KEY = "lu"; -static const char * GRAPHS_KEY = "g"; -static const char * TITLE_KEY = "t"; -static const char * XVY_KEY = "xvy"; -static const char * POINTS_DISPLAYED_KEY = "pd"; -static const char * SIZE_KEY = "sz"; -static const char * LABELS_KEY = "l"; -static const char * COLORS_KEY = "c"; -static const char * DATA_KEY = "d"; - #endif diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 0000000..0a133e5 --- /dev/null +++ b/src/Util.h @@ -0,0 +1,33 @@ +/* + =========================================================================================== + Utilities and constants + ------------------------------------------------------------------------------------------- + Plotter + v2.4.1 + https://github.com/devinaconley/arduino-plotter + by Devin Conley + =========================================================================================== +*/ + +#ifndef UTIL_H +#define UTIL_H + +// Constants +static const int CONFIG_INTERVAL = 50; +static const char* COLORS[] = { "green", "orange", "cyan", "yellow", "pink", "blue" }; + +// Transmission Keys +static const char* OUTER_KEY = "#"; +static const char* TIME_KEY = "t"; +static const char* NUM_GRAPH_KEY = "ng"; +static const char* LAST_UPDATED_KEY = "lu"; +static const char* GRAPHS_KEY = "g"; +static const char* TITLE_KEY = "t"; +static const char* XVY_KEY = "xvy"; +static const char* POINTS_DISPLAYED_KEY = "pd"; +static const char* SIZE_KEY = "sz"; +static const char* LABELS_KEY = "l"; +static const char* COLORS_KEY = "c"; +static const char* DATA_KEY = "d"; + +#endif \ No newline at end of file