/// <summary> /// Draws the graph. This single call updates the graph completely. /// </summary> /// Given a data access function which will return a double value for a given integer index of 0-entries this funtion will create /// a graph of the data using the color scheme previously set. This is a convenience method to simplify creating a graph with just /// one data line and a zero line. /// <param name="accessFunc">Access function which gives this method access to the data to graph.</param> /// <param name="max">Maximum value to be graphed.</param> /// <param name="min">Minimum value to be graphed.</param> /// <param name="entries">Number of entries accessible via the access function.</param> public void drawGraph(DataAccessFunc accessFunc, double max, double min, int entries) { reset(); drawZeroLine(max, min, _zeroLine); drawLineOnGraph(accessFunc, max, min, entries, _lineColor); Apply(); }
/// <summary> /// Draws a line on graph. /// </summary> /// <param name="accessFunc">Access function which gives this method access to the data to graph.</param> /// <param name="max">Maximum value to be graphed.</param> /// <param name="min">Minimum value to be graphed.</param> /// <param name="entries">Number of entries accessible via the access function.</param> /// <param name="color">Color of this line.</param> public void drawLineOnGraph(DataAccessFunc accessFunc, double max, double min, int entries, Color color) { _dirty = true; double scale = (double)_image.height / (max - min); int average = 1; int remainder = 0; int offset = 0; // If there are more entries than the _image can display calculate how many entries we should average. // If the division is not even calculate how many entries we should skip over and how often. if (entries > _image.width) { average = entries / _image.width; remainder = entries % _image.width; if (remainder > 0) { offset = (int)Math.Ceiling ((double)_image.width / (double)remainder); } } int prevY = 0; int index = 0; // For every x determine the range of ys that need to change and set them. for (int lx = 0; lx < _image.width;lx++) { double value = 0; // Don't exceed the boundaries of accessFunc. if (index + average > entries) { break; } // Average some values if we have more values than we have width. for (int i = 0; i < average; i++) { double data = accessFunc (index); if (Double.IsNaN (data)) { value = data; } value += data; index++; } if (Double.IsNaN (value)) { continue; } value /= average; // If we have too many entries and we can't easily average them drop values, but do it evenly so the holes aren't obvious. if (offset > 0 && lx % offset == 0 && remainder > 0) { index++; remainder--; } // Subtract min from the value to position it relative to y=0 and scale it to fit the image height. Cast it to an int. int y = (int)((value - min )* scale); // Calculate a contigous line from the previous y to the current position. This gives the graph an unbroken appearance. int startY = prevY; int endY = y; if (y < prevY) { startY = y; endY = prevY; } if (lx == 0) { endY = y; startY = y; } // Make sure the line is tall enough to be visible. if ((endY - startY) < _lineWidth) { endY += (_lineWidth - (endY - startY)); } // Never exceed the image height. We're dealing with floating point, so there is a certain inaccuracy in the scaling that // may allow our calculated y to exceed the image height by a pixel or two. if (endY > _image.height) { endY = _image.height; startY = _image.height - _lineWidth; } // Draw the line segment. for (int ly = startY; ly < endY; ly++) _image.SetPixel(lx,ly,color); prevY = y; } }
/// <summary> /// Draws the graph. This single call updates the graph completely. /// </summary> /// Given a data access function which will return a double value for a given integer index of 0-entries this funtion will create /// a graph of the data using the color scheme previously set. This is a convenience method to simplify creating a graph with just /// one data line and a zero line. /// <param name="accessFunc">Access function which gives this method access to the data to graph.</param> /// <param name="max">Maximum value to be graphed.</param> /// <param name="min">Minimum value to be graphed.</param> /// <param name="entries">Number of entries accessible via the access function.</param> public void drawGraph(DataAccessFunc accessFunc, double max, double min, int entries) { reset (); drawZeroLine (max, min, _zeroLine); drawLineOnGraph (accessFunc, max, min, entries, _lineColor); Apply (); }
/// <summary> /// Draws a line on graph. /// </summary> /// <param name="accessFunc">Access function which gives this method access to the data to graph.</param> /// <param name="max">Maximum value to be graphed.</param> /// <param name="min">Minimum value to be graphed.</param> /// <param name="entries">Number of entries accessible via the access function.</param> /// <param name="color">Color of this line.</param> public void drawLineOnGraph(DataAccessFunc accessFunc, double max, double min, int entries, Color color) { _dirty = true; double scale = (double)_image.height / (max - min); int average = 1; int remainder = 0; int offset = 0; // If there are more entries than the _image can display calculate how many entries we should average. // If the division is not even calculate how many entries we should skip over and how often. if (entries > _image.width) { average = entries / _image.width; remainder = entries % _image.width; if (remainder > 0) { offset = (int)Math.Ceiling((double)_image.width / (double)remainder); } } int prevY = 0; int index = 0; // For every x determine the range of ys that need to change and set them. for (int lx = 0; lx < _image.width; lx++) { double value = 0; // Don't exceed the boundaries of accessFunc. if (index + average > entries) { break; } // Average some values if we have more values than we have width. for (int i = 0; i < average; i++) { double data = accessFunc(index); if (Double.IsNaN(data)) { value = data; } value += data; index++; } if (Double.IsNaN(value)) { continue; } value /= average; // If we have too many entries and we can't easily average them drop values, but do it evenly so the holes aren't obvious. if (offset > 0 && lx % offset == 0 && remainder > 0) { index++; remainder--; } // Subtract min from the value to position it relative to y=0 and scale it to fit the image height. Cast it to an int. int y = (int)((value - min) * scale); // Calculate a contigous line from the previous y to the current position. This gives the graph an unbroken appearance. int startY = prevY; int endY = y; if (y < prevY) { startY = y; endY = prevY; } if (lx == 0) { endY = y; startY = y; } // Make sure the line is tall enough to be visible. if ((endY - startY) < _lineWidth) { endY += (_lineWidth - (endY - startY)); } // Never exceed the image height. We're dealing with floating point, so there is a certain inaccuracy in the scaling that // may allow our calculated y to exceed the image height by a pixel or two. if (endY > _image.height) { endY = _image.height; startY = _image.height - _lineWidth; } // Draw the line segment. for (int ly = startY; ly < endY; ly++) { _image.SetPixel(lx, ly, color); } prevY = y; } }