#include "Graph.h" Graph::Graph(Ecran * _ecran, Graph_Param _gp) { ecran = _ecran; gp = _gp; } /* INITIALCODE FROM (C) D L BIRD This function will draw a graph on a ePaper/TFT/LCD display using data from an array containing data to be graphed. The variable 'max_readings' determines the maximum number of data elements for each array. Call it with the following parametric data: x_pos-the x axis top-left position of the graph y_pos-the y-axis top-left position of the graph, e.g. 100, 200 would draw the graph 100 pixels along and 200 pixels down from the top-left of the screen width-the width of the graph in pixels height-height of the graph in pixels Y1_Max-sets the scale of plotted data, for example 5000 would scale all data to a Y-axis of 5000 maximum data_array1 is parsed by value, externally they can be called anything else, e.g. within the routine it is called data_array1, but externally could be temperature_readings gp.auto_scale-a logical value (TRUE or FALSE) that switches the Y-axis autoscale On or Off barchart_on-a logical value (TRUE or FALSE) that switches the drawing mode between barhcart and line graph barchart_colour-a sets the gp.title and graph plotting colour If called with Y!_Max value of 500 and the data never goes above 500, then autoscale will retain a 0-500 Y scale, if on, the scale increases/decreases to match the data. gp.auto_scale_margin, e.g. if set to 1000 then autoscale increments the scale by 1000 steps. Changed to be an object add x gp.title multiple gp.series colors for gp.series allow 2 scales use Graph_Param use enums add thickness add fill line mode */ // float DataArray[], int readings void Graph::draw(int readings, int x_pos, int y_pos, int gwidth, int gheight) { if (debug) Serial.println("B - graph::draw"); float maxYscale[2] = {-10000, -10000}; float minYscale[2] = {10000, 10000}; int last_x, last_y; float x2, y2; if (gp.auto_scale == DYNAMIC_SCALE) { for (int s = 0; s < gp.nb_series; s++) { int ref = 0; if (gp.show_2_scales) { ref = s; } for (int i = 1; i < readings; i++ ) { if (gp.series[s].data[i] >= maxYscale[ref]) maxYscale[ref] = gp.series[s].data[i]; if (gp.series[s].data[i] <= minYscale[ref]) minYscale[ref] = gp.series[s].data[i]; } } for (int s = 0; s < gp.nb_series; s++) { maxYscale[s] = round(maxYscale[s] + gp.auto_scale_margin); // Auto scale the graph and round to the nearest value defined, default was gp.Y_Max[ref] gp.Y_Max[s] = round(maxYscale[s] + 0.5); if (minYscale[s] != s) minYscale[s] = round(minYscale[s] - gp.auto_scale_margin); // Auto scale the graph and round to the nearest value defined, default was gp.Y_Min[ref] gp.Y_Min[s] = round(minYscale[s]); } } if (debug) Serial.println(" graph::draw " + String(gp.nb_series) + " " + String(x_pos) + " " + String(y_pos) + " " + String(gwidth) + " " + String(gheight) + " " //+ String(last_x) + " " + String(last_y) + " " + String(gp.Y_Min[0]) + " " + String(gp.Y_Max[0]) + " " + String(gp.Y_Min[1]) + " " + String(gp.Y_Max[1]) ); ecran->_display->drawRect(x_pos, y_pos, gwidth + 3, gheight + 2, GxEPD_BLACK); ecran->drawString(x_pos + gwidth / 2, y_pos - 13, gp.title, CENTER, GxEPD_BLACK); // Draw the data for (int s = 0; s < gp.nb_series; s++) { int ref = 0; if (gp.show_2_scales == TWO_SCALES) { ref = s; } // Draw the graph last_x = x_pos; last_y = y_pos + (gp.Y_Max[ref] - constrain(gp.series[ref].data[1], gp.Y_Min[ref], gp.Y_Max[ref])) / (gp.Y_Max[ref] - gp.Y_Min[ref]) * gheight; if (debug) Serial.print("Data to draw : S" + String(s) + " "); for (int gx = 0; gx < readings; gx++) { if (debug) Serial.print(gp.series[s].data[gx]); if (debug) Serial.print(" "); y2 = y_pos + (gp.Y_Max[ref] - constrain(gp.series[s].data[gx], gp.Y_Min[ref], gp.Y_Max[ref])) / (gp.Y_Max[ref] - gp.Y_Min[ref]) * gheight + 1; if (gp.series[s].chart_type == TYPE_BARCHART) { x2 = x_pos + gx * (gwidth / readings) + 2; ecran->_display->fillRect(x2, y2, (gwidth / readings) - 2, y_pos + gheight - y2 + 2, gp.series[s].color ); } else { x2 = x_pos + gx * gwidth / (readings - 1) + 1; // max_readings is the global variable that sets the maximum data that can be plotted if (gp.series[s].chart_type == TYPE_FILL_LINE) { if (last_y < y2) { ecran->_display->fillTriangle(last_x, last_y, last_x, y2, x2, y2, gp.series[s].color); } else { ecran->_display->fillTriangle(last_x, last_y, x2, last_y, x2, y2, gp.series[s].color); } float y0 = min((float) x2, (float) last_y); ecran->_display->fillRect(x2, y0, (gwidth / readings), y_pos + gheight - y0 + 2, gp.series[s].color); ecran->_display->drawLine(last_x, last_y, x2, y2, GxEPD_BLACK); } else { if (gp.series[s].thickness > 1) { ecran->drawAngledLine(last_x, last_y, x2, y2, gp.series[s].thickness, gp.series[s].color); } else { ecran->_display->drawLine(last_x, last_y, x2, y2, gp.series[s].color); } } } last_x = x2; last_y = y2; } if (debug) Serial.println(" "); } if (debug) Serial.println(" "); for (int spacing = 0; spacing <= gp.y_minor_axis; spacing++) { for (int j = 0; j < gp.number_of_dashes; j++) { // Draw dashed graph grid lines if (spacing < gp.y_minor_axis) ecran->_display->drawFastHLine((x_pos + 3 + j * gwidth / gp.number_of_dashes), y_pos + (gheight * spacing / gp.y_minor_axis), gwidth / (2 * gp.number_of_dashes), GxEPD_BLACK); } for (int s = 0; s < (gp.show_2_scales ? 2 : 1); s++) { int color = (gp.show_2_scales == TWO_SCALES ? gp.series[s].color : GxEPD_BLACK); int xp = (s == 0 ? 0 : gwidth + 10); if ((gp.Y_Max[s] - (float)(gp.Y_Max[s] - gp.Y_Min[s]) / gp.y_minor_axis * spacing) < 5) { ecran->drawString(x_pos + xp - 1, y_pos + gheight * spacing / gp.y_minor_axis - 5, String((gp.Y_Max[s] - (float)(gp.Y_Max[s] - gp.Y_Min[s]) / gp.y_minor_axis * spacing + 0.01), 1), (s == 0 ? RIGHT : LEFT), color ); } else { if (gp.Y_Min[s] < 1 && gp.Y_Max[s] < 10) ecran->drawString(x_pos + xp - 1, y_pos + gheight * spacing / gp.y_minor_axis - 5, String((gp.Y_Max[s] - (float)(gp.Y_Max[s] - gp.Y_Min[s]) / gp.y_minor_axis * spacing + 0.01), 1), (s == 0 ? RIGHT : LEFT), color); else ecran->drawString(x_pos + xp - 2, y_pos + gheight * spacing / gp.y_minor_axis - 5, String((gp.Y_Max[s] - (float)(gp.Y_Max[s] - gp.Y_Min[s]) / gp.y_minor_axis * spacing + 0.01), 0), (s == 0 ? RIGHT : LEFT), color); } } } // for (int i = 0; i <= 2; i++) { // ecran->drawString(15 + x_pos + gwidth / 3 * i, y_pos + gheight + 3, String(i), LEFT); // } ecran->drawString(x_pos + gwidth / 2, y_pos + gheight + 14, gp.x_title, RIGHT); if (debug) Serial.println("E - graph::draw"); }