/// <summary> /// Generates several sub graphs with progressively fewer number of data points /// This allows us to redraw the graph quickly during manipulation /// </summary> private void GenerateSubGraphs() { if (GraphData != null) { // Determine how many sub graphs we need int numGraphs = 1; int currNumPoints = GraphData.Count; while (currNumPoints > 64) { currNumPoints = currNumPoints >> 1; numGraphs++; } // Create empty sub graphs graphData = new List <PointF> [numGraphs]; currNumPoints = GraphData.Count; for (int i = 0; i < graphData.Length; i++) { graphData[i] = new List <PointF>(currNumPoints); currNumPoints = currNumPoints % 2 == 0 ? currNumPoints >> 1 : currNumPoints >> 1 + 1; } // This is the source of the graph's efficiency, here we take the users input and generate several other // data inputs with progressively less accurate (due to rounding) depictions of the graph data, but each // with 1/2 as many points as the last graph we generated. float[] totalX = new float[graphData.Length]; float[] totalY = new float[graphData.Length]; float lastX = float.MinValue; for (int i = 0; i < GraphData.Count; i++) { IGraphData point = GraphData[i]; float x = (float)point.GetX(); float y = (float)WorldToScreenYCoord(point.GetY()); bool isLastPoint = i == GraphData.Count - 1; if (x < lastX) { throw new ArgumentException("GraphData must be sorted by ascending X values"); } lastX = x; // Add the first and last points to all graphData sets if (i == 0) { for (int j = 1; j < graphData.Length; j++) { graphData[j].Add(new PointF(x, y)); } } graphData[0].Add(new PointF(x, y)); // Add all data points to the first graphData set for (int j = 1; j < graphData.Length; j++) // For the rest of the sets, add averages of the points { totalX[j] += x; totalY[j] += y; // We are dealing with powers of 2, so we can do a more efficient mod operation '%' int mask = (1 << j) - 1; int remainder = (mask & i); if (remainder == 0 || isLastPoint) { int divisor = remainder == 0 ? mask + 1 : remainder; graphData[j].Add(new PointF(totalX[j] / divisor, totalY[j] / divisor)); totalX[j] = totalY[j] = 0; } } } } }